ทำไม. json () ถึงคืนคำสัญญา?


117

fetch()เมื่อเร็ว ๆ นี้ฉันยุ่งกับapi และสังเกตเห็นบางอย่างที่แปลกไปหน่อย

let url = "http://jsonplaceholder.typicode.com/posts/6";

let iterator = fetch(url);

iterator
  .then(response => {
      return {
          data: response.json(),
          status: response.status
      }
  })
  .then(post => document.write(post.data));
;

post.dataส่งคืนPromiseวัตถุ http://jsbin.com/wofulo/2/edit?js,output

อย่างไรก็ตามหากเขียนเป็น:

let url = "http://jsonplaceholder.typicode.com/posts/6";

let iterator = fetch(url);

iterator
  .then(response => response.json())
  .then(post => document.write(post.title));
;

postนี่คือมาตรฐานObjectที่คุณสามารถเข้าถึงแอตทริบิวต์หัวเรื่อง http://jsbin.com/wofulo/edit?js,output

ดังนั้นคำถามของฉันคือทำไมresponse.jsonส่งคืนคำสัญญาในตัวอักษรของวัตถุ แต่ส่งคืนค่าหากเพิ่งส่งคืน


1
สิ่งนี้สมเหตุสมผลเมื่อคุณพิจารณาresponse.json()ว่าสัญญาอาจถูกปฏิเสธหากการตอบกลับไม่ใช่ JSON ที่ถูกต้อง
ssube

1
ค่าจะถูกส่งกลับเนื่องจากสัญญาได้รับการแก้ไขโดยส่งค่าใน response.json () ตอนนี้ค่าพร้อมใช้งานแล้วในวิธีการ
Jose Hermosilla Rodrigo

คำตอบ:


168

ทำไมresponse.jsonคืนคำสัญญา?

เพราะคุณจะได้รับresponseทันทีที่ส่วนหัวทั้งหมดมาถึง การโทร.json()ทำให้คุณได้รับสัญญาอีกครั้งสำหรับเนื้อหาของการตอบสนอง http ที่ยังไม่ได้โหลด ดูเพิ่มเติมเหตุใดออบเจ็กต์ตอบกลับจาก JavaScript fetch API จึงเป็นสัญญา .

เหตุใดฉันจึงได้รับค่านี้หากฉันคืนสัญญาจากthenผู้จัดการ

เพราะนั่นคือวิธีการทำงานของสัญญา ความสามารถในการคืนสัญญาจากการโทรกลับและนำไปใช้เป็นคุณสมบัติที่เกี่ยวข้องมากที่สุดทำให้สามารถเชื่อมโยงกันได้โดยไม่ต้องซ้อนกัน

คุณสามารถใช้ได้

fetch(url).then(response => 
    response.json().then(data => ({
        data: data,
        status: response.status
    })
).then(res => {
    console.log(res.status, res.data.title)
}));

หรือวิธีการอื่นใดในการเข้าถึงคำสัญญาก่อนหน้านี้จะส่งผลให้เกิดห่วงโซ่. แล้ว ()เพื่อรับสถานะการตอบสนองหลังจากที่รอร่างกาย json


1
ดูเหมือนว่าแปลกที่ฉันไม่สามารถรอให้ข้อมูลกลับมาโดยใช้ Promise ได้และเมื่อมาถึงแล้วจะแปลงเป็น json? หรือบางทีในกรณีนั้นฉันสามารถใช้JSON.parse()แทนres.json()??
Kokodoko

8
@Kokodoko res.json()โดยทั่วไปเป็นทางลัดสำหรับres.text().then(JSON.parse). ทั้งสองรอข้อมูลโดยใช้สัญญาและแยกวิเคราะห์ json
Bergi

@Bergi สวัสดีขอโทษที่ฉันต้องเผชิญกับความสับสนนั่นคือเมื่อใช้ (res => res.json ()) เราจะส่งคำขออื่นเพื่อรับ JSON?
mirzhal

1
@mirzhal ไม่มีขอเป็นอย่างอื่น มันกำลังอ่านคำตอบที่เหลือ (แบบอะซิงโครนัส!)
Bergi

14

ความแตกต่างนี้เกิดจากพฤติกรรมของสัญญามากกว่าfetch()โดยเฉพาะ

เมื่อการ.then()โทรกลับส่งกลับเพิ่มเติมPromiseการ.then()โทรกลับครั้งต่อไปในห่วงโซ่จะผูกพันกับสัญญานั้นเป็นหลักโดยได้รับการแก้ไขหรือปฏิเสธการปฏิบัติตามและคุณค่า

ตัวอย่างข้อมูลที่ 2 สามารถเขียนเป็น:

iterator.then(response =>
    response.json().then(post => document.write(post.title))
);

ทั้งในรูปแบบนี้และคุณค่าของการให้บริการโดยสัญญากลับมาจากpostresponse.json()


อย่างไรก็ตามเมื่อคุณส่งคืนค่าธรรมดาObjectให้.then()พิจารณาว่าผลลัพธ์ที่ประสบความสำเร็จและแก้ไขตัวเองทันทีคล้ายกับ:

iterator.then(response =>
    Promise.resolve({
      data: response.json(),
      status: response.status
    })
    .then(post => document.write(post.data))
);

postในกรณีนี้เป็นเพียงObjectที่คุณสร้างขึ้นซึ่งถือPromiseในของdataสถานที่ให้บริการ การรอให้คำสัญญานั้นเป็นจริงนั้นยังไม่สมบูรณ์


7

นอกจากนี้สิ่งที่ช่วยให้ฉันเข้าใจสถานการณ์เฉพาะนี้ที่คุณอธิบายไว้คือเอกสาร Promise API โดยเฉพาะที่อธิบายว่าวิธีการที่สัญญาส่งคืนthenจะได้รับการแก้ไขแตกต่างกันอย่างไรขึ้นอยู่กับสิ่งที่ตัวจัดการ fnส่งคืน:

ถ้าฟังก์ชันตัวจัดการ:

  • ส่งคืนค่าสัญญาที่ส่งคืนจากนั้นจะได้รับการแก้ไขโดยมีค่าที่ส่งคืนเป็นค่าของมัน
  • แสดงข้อผิดพลาดสัญญาที่ส่งกลับมาจะถูกปฏิเสธโดยมีข้อผิดพลาดที่ถูกโยนเป็นมูลค่า
  • ส่งคืนสัญญาที่ได้รับการแก้ไขแล้วสัญญาที่ส่งคืนจะได้รับการแก้ไขโดยมีมูลค่าของสัญญานั้นเป็นมูลค่า
  • ส่งคืนคำสัญญาที่ปฏิเสธไปแล้วสัญญาที่ส่งกลับมาจะถูกปฏิเสธโดยมีมูลค่าของสัญญานั้นเป็นมูลค่า
  • ส่งคืนวัตถุสัญญาที่รอดำเนินการอื่นการแก้ไข / ปฏิเสธคำสัญญาที่ส่งคืนในภายหลังจะตามมาจากการแก้ไข / ปฏิเสธสัญญาที่ผู้จัดการส่งคืน นอกจากนี้มูลค่าของสัญญาที่ส่งคืนในตอนนั้นจะเท่ากับมูลค่าของสัญญาที่ผู้จัดการส่งคืน

5

นอกเหนือจากคำตอบข้างต้นต่อไปนี้คือวิธีจัดการกับการตอบสนอง 500 series จาก api ของคุณซึ่งคุณได้รับข้อความแสดงข้อผิดพลาดที่เข้ารหัสใน json:

function callApi(url) {
  return fetch(url)
    .then(response => {
      if (response.ok) {
        return response.json().then(response => ({ response }));
      }

      return response.json().then(error => ({ error }));
    })
  ;
}

let url = 'http://jsonplaceholder.typicode.com/posts/6';

const { response, error } = callApi(url);
if (response) {
  // handle json decoded response
} else {
  // handle json decoded 500 series response
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.