ข้อมูลเบื้องต้นเกี่ยวกับ fetch()

fetch() ให้คุณสร้างคำขอเครือข่ายที่คล้ายกับ XMLHttpRequest (XHR) ได้ ความแตกต่างหลักๆ ก็คือ API การดึงข้อมูลจะใช้ Promises ซึ่งมี API ที่เรียบง่ายกว่าเพื่อช่วยคุณหลีกเลี่ยงโค้ดเรียกกลับที่ซับซ้อนใน XMLHttpRequest API

การสนับสนุนเบราว์เซอร์

  • 42
  • 14
  • 39
  • 10.1

แหล่งที่มา

หากคุณไม่เคยใช้ Promises มาก่อน โปรดดูข้อมูลเบื้องต้นเกี่ยวกับ JavaScript Promises

คำขอการดึงข้อมูลพื้นฐาน

นี่คือตัวอย่างที่นำไปใช้งานด้วย XMLHttpRequest ตามด้วย fetch เราต้องการ URL, รับคำตอบ และแยกวิเคราะห์เป็น JSON

XMLHttpRequest

XMLHttpRequest ต้องการผู้ฟัง 2 คนเพื่อจัดการกรณีความสำเร็จและข้อผิดพลาด รวมถึงเรียกใช้ open() และ send() ตัวอย่างจากเอกสาร MDN

function reqListener() {
    var data = JSON.parse(this.responseText);
    console.log(data);
}

function reqError(err) {
    console.log('Fetch Error :-S', err);
}

var oReq = new XMLHttpRequest();
oReq.onload = reqListener;
oReq.onerror = reqError;
oReq.open('get', './api/some.json', true);
oReq.send();

ดึงข้อมูล

คำขอดึงข้อมูลของเรามีลักษณะดังนี้

fetch('./api/some.json')
  .then(
  function(response) {
    if (response.status !== 200) {
      console.log('Looks like there was a problem. Status Code: ' +
        response.status);
      return;
    }

    // Examine the text in the response
    response.json().then(function(data) {
      console.log(data);
    });
  }
  )
  .catch(function(err) {
    console.log('Fetch Error :-S', err);
  });

คำขอ fetch() ต้องการการเรียกเพียงครั้งเดียวเพื่อการทำงานเหมือนกับตัวอย่าง XHR ในการประมวลผลการตอบกลับ ก่อนอื่นเราจะตรวจสอบว่าสถานะการตอบกลับเป็น 200 แล้วแยกวิเคราะห์การตอบกลับเป็น JSON การตอบกลับคำขอ fetch() จะเป็นออบเจ็กต์สตรีม ซึ่งหมายความว่าหลังจากที่เราเรียกใช้เมธอด json() แล้ว ระบบจะแสดงผล Promise สตรีมจะเกิดขึ้นแบบไม่พร้อมกัน

ข้อมูลเมตาของการตอบกลับ

ตัวอย่างก่อนหน้านี้แสดงสถานะของออบเจ็กต์การตอบกลับ และวิธีแยกวิเคราะห์การตอบกลับเป็น JSON วิธีจัดการข้อมูลเมตาอื่นๆ ที่คุณอาจต้องการเข้าถึง เช่น ส่วนหัว มีดังนี้

fetch('users.json').then(function(response) {
    console.log(response.headers.get('Content-Type'));
    console.log(response.headers.get('Date'));

    console.log(response.status);
    console.log(response.statusText);
    console.log(response.type);
    console.log(response.url);
});

ประเภทการตอบสนอง

เมื่อส่งคำขอดึงข้อมูล การตอบกลับจะได้รับ response.type เป็น response.type "basic", "cors" หรือ "opaque" types เหล่านี้จะแสดงที่มาของทรัพยากร และคุณสามารถใช้สิ่งนี้เพื่อระบุวิธีจัดการออบเจ็กต์การตอบกลับ

เมื่อเบราว์เซอร์ขอทรัพยากรในต้นทางเดียวกัน การตอบกลับจะมีประเภท basic ที่มีข้อจำกัดเกี่ยวกับสิ่งที่จะดูจากการตอบกลับได้

หากมีการสร้างคำขอสำหรับทรัพยากรในต้นทางอื่น และต้นทางนั้นแสดงผลส่วนหัวของ COR ประเภทจะเป็น cors การตอบกลับ cors คล้ายกับคำตอบ basic แต่จะจำกัดส่วนหัวที่คุณดูได้สำหรับ Cache-Control, Content-Language, Content-Type, Expires, Last-Modified และ Pragma

การตอบกลับ opaque รายการมาจากต้นทางอื่นที่ไม่ได้แสดงส่วนหัว CORS หากการตอบกลับไม่ชัดเจน เราจะไม่สามารถอ่านข้อมูลที่ส่งคืนหรือดูสถานะของคำขอได้ ซึ่งหมายความว่าคุณจะไม่สามารถตรวจสอบได้ว่าคำขอนั้นสำเร็จหรือไม่

คุณกำหนดโหมดสำหรับคำขอดึงข้อมูลเพื่อให้แก้ไขปัญหาได้เฉพาะคำขอบางประเภท โหมดที่คุณสามารถตั้งค่ามีดังนี้

  • same-origin ประสบความสำเร็จสำหรับคำขอสำหรับเนื้อหาจากต้นทางเดียวกันเท่านั้น และปฏิเสธคำขออื่นๆ ทั้งหมด
  • cors อนุญาตคำขอเนื้อหาในต้นทางเดียวกันและต้นทางอื่นๆ ที่แสดงส่วนหัว COR ที่เหมาะสม
  • cors-with-forced-preflight จะตรวจสอบล่วงหน้าก่อนส่งคำขอใดๆ
  • no-cors มีไว้เพื่อส่งคำขอไปยังต้นทางอื่นๆ ที่ไม่มีส่วนหัว CORS และได้ผลลัพธ์เป็น opaque แต่ตามที่ระบุไว้ การดำเนินการดังกล่าวไม่สามารถทำได้ในขอบเขตรวมทั้งหมดของกรอบเวลาในขณะนี้

หากต้องการกำหนดโหมด ให้เพิ่มออบเจ็กต์ตัวเลือกเป็นพารามิเตอร์ที่ 2 ในคำขอ fetch และกำหนดโหมดในออบเจ็กต์ดังกล่าว ดังนี้

fetch('http://some-site.com/cors-enabled/some.json', {mode: 'cors'})
    .then(function(response) {
    return response.text();
    })
    .then(function(text) {
    console.log('Request successful', text);
    })
    .catch(function(error) {
    log('Request failed', error)
    });

การสานสัมพันธ์ด้วยสัญญา

ฟีเจอร์ที่ยอดเยี่ยมอย่างหนึ่งของสัญญาคือความสามารถในการผูกไว้ด้วยกัน สำหรับ fetch() การดำเนินการนี้จะช่วยให้คุณแชร์ตรรกะข้ามคำขอดึงข้อมูลได้

หากใช้ JSON API คุณต้องตรวจสอบสถานะและแยกวิเคราะห์ JSON สำหรับแต่ละคำตอบ คุณลดความซับซ้อนของโค้ดได้โดยกำหนดสถานะและการแยกวิเคราะห์ JSON แยกระหว่างฟังก์ชันที่แสดงผลสัญญา และใช้คำขอดึงข้อมูลเพื่อจัดการเฉพาะข้อมูลสุดท้ายและกรณีข้อผิดพลาด

function status(response) {
    if (response.status >= 200 && response.status < 300) {
    return Promise.resolve(response)
    } else {
    return Promise.reject(new Error(response.statusText))
    }
}

function json(response) {
    return response.json()
}

fetch('users.json')
    .then(status)
    .then(json)
    .then(function(data) {
    console.log('Request succeeded with JSON response', data);
    }).catch(function(error) {
    console.log('Request failed', error);
    });

ตัวอย่างนี้กำหนดฟังก์ชัน status ที่จะตรวจสอบ response.status และแสดงผล Promise ที่แก้ไขแล้วเป็น Promise.resolve() หรือ Promise ที่ถูกปฏิเสธเป็น Promise.reject() นี่คือวิธีการแรกที่มีการเรียกใช้ในเชน fetch()

หาก Promise แก้ไขได้ สคริปต์จะเรียกเมธอด json() ซึ่งแสดงผล Promise รายการที่ 2 จากการเรียกใช้ response.json() และสร้างออบเจ็กต์ที่มี JSON ที่แยกวิเคราะห์แล้ว หากการแยกวิเคราะห์ไม่สำเร็จ ระบบจะปฏิเสธ Promise และระบบจะทำงาน

โครงสร้างนี้ช่วยให้คุณแชร์ตรรกะในคำขอดึงข้อมูลทั้งหมดซึ่งทำให้ดูแลรักษา อ่าน และทดสอบโค้ดได้ง่ายขึ้น

คำขอ POST

บางครั้งเว็บแอปจำเป็นต้องเรียก API ด้วยเมธอด POST และรวมพารามิเตอร์บางอย่างไว้ในเนื้อหาของคำขอ หากต้องการดำเนินการดังกล่าว ให้ตั้งค่าพารามิเตอร์ method และ body ในตัวเลือก fetch() ดังนี้

fetch(url, {
    method: 'post',
    headers: {
        "Content-type": "application/x-www-form-urlencoded; charset=UTF-8"
    },
    body: 'foo=bar&lorem=ipsum'
    })
    .then(json)
    .then(function (data) {
    console.log('Request succeeded with JSON response', data);
    })
    .catch(function (error) {
    console.log('Request failed', error);
    });

ส่งข้อมูลเข้าสู่ระบบพร้อมคำขอดึงข้อมูล

หากต้องการส่งคำขอดึงข้อมูลด้วยข้อมูลเข้าสู่ระบบ เช่น คุกกี้ ให้ตั้งค่า credentials ของคำขอเป็น "include" ดังนี้

fetch(url, {
    credentials: 'include'
})