Hi,

ฉันสัญญาว่าคุณจะได้รับบทสรุปของโพสต์นี้ในตอนท้าย

การดำเนินการแบบอะซิงโครนัสถือเป็นหัวใจสำคัญของการนำการโต้ตอบไปใช้ในแอปพลิเคชัน JavaScript สมัยใหม่ สิ่งเหล่านี้จะใช้เมื่อทำการเรียก API, คำขอเครือข่าย หรือแม้แต่ผ่านฟังก์ชันการหน่วงเวลาพื้นฐาน

การดำเนินการแบบอะซิงโครนัสใช้สัญญา ฟังก์ชันการโทรกลับ หรือ async/await โดยทั่วไป การดำเนินการเหล่านี้เป็นเอกพจน์และไม่จำเป็นต้องรวมการดำเนินการอะซิงก์หลายรายการไว้ในที่เดียว

เมื่อเร็วๆ นี้ ฉันเริ่มทำงานเพื่อสร้างบริการรวบรวมที่ใช้ API ของบุคคลที่สามหลายรายการและรวบรวมข้อมูลผลลัพธ์ ในโพสต์นี้ เราจะเรียนรู้วิธีสร้างคำขอ async พร้อมกันโดยใช้ Promise.all() ใน JavaScript นอกจากนี้ เราจะได้เรียนรู้วิธีจำกัดคำขอเหล่านี้ให้ทำงานในบางกลุ่ม/บางส่วนในแต่ละครั้ง

ใช้ Promise.all()

ฟังก์ชันอะซิงก์เพื่อดึงข้อมูลจาก API โดยทั่วไปจะมีลักษณะดังนี้:

async function fetchData() {
  const res = await axios.get("./names.json");
  console.log(res.data);
}

ที่นี่เราใช้ Axios ซึ่งเป็นไคลเอนต์ HTTP ตามสัญญา เพื่อสร้างคำขอ HTTP เพื่อดึงข้อมูลในไฟล์ json ในเครื่อง อีกทางเลือกหนึ่งนอกเหนือจากการใช้ async/await คือการใช้เมธอด .then() ของสัญญา

ด้วย Promise.all() เราจัดการคำขอที่คล้ายกันหลายรายการพร้อมกัน และส่งคืนการตอบกลับแบบรวมเพียงรายการเดียว Promise.all() รับคำสัญญาที่สามารถทำซ้ำได้ (อาร์เรย์) โดยจะส่งคืนอาร์เรย์ที่มีการแก้ไขสัญญาแต่ละรายการในลำดับเดียวกัน

หากสัญญาใดๆ ใน Promise.all() ถูกปฏิเสธ การรวมสัญญาจะถูกปฏิเสธ นี่คือตัวอย่างด้านล่าง:

const fetchNames = async () => {
    try {
      const res = await Promise.all([
        axios.get("./names.json"),
        axios.get("./names-mid.json"),
        axios.get("./names-old.json")
      ]);
      const data = res.map((res) => res.data);
      console.log(data.flat());
    } catch {
      throw Error("Promise failed");
    }
  };

ตัวอย่างโค้ดนี้ซับซ้อนกว่าและอยู่ในบล็อก try/catch เพื่อตรวจจับความล้มเหลวในการแก้ไขสัญญา

Promise.all() ไม่ได้แก้ไขสัญญาและรวมเฉพาะสัญญาไว้ในอาร์เรย์ที่มีความละเอียดเดียวเท่านั้น ฉันจะตัดเรื่องไร้สาระออก ซึ่งหมายความว่าคุณต้องใช้ Promise.all() กับ await หรือ .then() เพื่อแก้ไข 😁

.flat() เป็นวิธีอาร์เรย์ที่มีประโยชน์ซึ่งจะทำให้อาร์เรย์เรียบขึ้น ก่อนหน้านี้จะทำได้ด้วยฟังก์ชัน forloop หรือลด

อีกทางเลือกหนึ่งที่มี fetch API มีลักษณะดังนี้:

const fetchNames = async () => {
      try {
        const res = await Promise.all([
          fetch("./names.json"),
          fetch("./names-mid.json"),
          fetch("./names-old.json")
        ]);
        const data = await Promise.all(res.map(r => r.json()))
        console.log(data.flat());
      } catch {
        throw Error("Promise failed");
      }
};

หลังจากใช้ fetch() แล้ว .json() จะต้องแยกวิเคราะห์การตอบสนอง และยังคืนสัญญาอีกด้วย! สัญญาไว้หลายข้อ นี่กำลังกลายเป็นเทเลโนเวลาแล้ว!

จำเป็นต้องมีสัญญาอื่นทั้งหมดเพื่อรวบรวมคำตอบ

เพื่อให้เข้าใจผลกระทบของ Promise.all ได้ดีขึ้น เราจะสร้างฟังก์ชันจับเวลาที่แก้ไขสัญญาหลังจากช่วงระยะเวลาหนึ่ง

การสังเกตด้วยฟังก์ชันจับเวลา

เราจะสร้างคำสัญญาสามประการด้วย:

const promise1 = new Promise((resolve, reject) => {
    setTimeout(() => {
      const newValue = Math.floor(Math.random() * 20);
      resolve(newValue);
    }, 5000);
  });

  const promise2 = new Promise((resolve, reject) => {
    setTimeout(() => {
      const newValue = Math.floor(Math.random() * 20);
      resolve(newValue);
    }, 8000);
  });

  const promise3 = new Promise((resolve, reject) => {
    setTimeout(() => {
      const newValue = Math.floor(Math.random() * 20);
      resolve(newValue);
    }, 2000);
  });

แต่ละคำสัญญาจะได้รับการแก้ไขในเวลาที่ต่างกัน 5, 8 และ 2 วินาทีตามลำดับ

การเรียกแต่ละฟังก์ชันแยกกันใน async/await จะส่งกลับค่าที่ได้รับการแก้ไขของแต่ละฟังก์ชันหลังจากระยะเวลาที่กำหนด การรวมผลลัพธ์จะต้องมีการดำเนินการ JavaScript เพิ่มเติมเพื่อสร้างอาร์เรย์

การโทรทั้งหมดใน Promise.all() จะเป็นการแก้ปัญหาทั้งหมดพร้อมกัน ในกรณีนี้ เวลาที่ฟังก์ชันต้องใช้เวลาดำเนินการมากที่สุดคือ 8 วินาที

เมื่อใช้ Promise.all() เรามี:

const fetchAsyncData = async () => {
    const res = await Promise.all([promise1, promise2, promise3]);
    console.log(res);
  };

มันเป็นโค้ดที่มีประสิทธิภาพและสะอาดกว่าสำหรับฉัน 😃

การจำกัดการเกิดพร้อมกัน

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

แพ็คเกจ npm ที่มีประโยชน์ที่ฉันพบว่าทำคือ "p-limit"

คุณสามารถเพิ่มลงในโครงการของคุณโดยใช้ npm หรือเส้นด้ายด้วย:

npm install p-limit

yarn add p-limit

สร้างขีดจำกัดและระบุการนับพร้อมกันด้วย:

import pLimit from 'p-limit'

const limit = pLimit(2)

ใช้ขีดจำกัดนี้ในสัญญากับ:

const res = await Promise.all([
      limit(() => promise1),
      limit(() => promise2),
      limit(() => promise3)
]);

บล็อกนี้รันสองสัญญาในแต่ละครั้ง

นี่คือ "ลิงก์ CodeSandbox ที่มีบล็อกโค้ดทั้งหมดที่ทำงานในแอป React และบันทึกข้อมูลไปยังคอนโซล"

สรุป

เช่นเดียวกับคำสัญญาใน JavaScript คุณรู้ว่าบทสรุปนี้กำลังจะมาถึง ฉันบอกคุณตั้งแต่ต้นแล้ว นี่เป็นเหมือนสัญญาใน JavaScript ในโพสต์นี้ เราได้ดูวิธีการแก้ไขสัญญาแบบรวมโดยใช้ Promise.all() และจำกัดการทำงานพร้อมกันตามที่จำเป็นโดยใช้ p-limit

วิธีการตรวจสอบสัญญาอื่น ๆ ได้แก่ :

นี่คือการพัฒนาให้ดีขึ้น 🥂

วิลเลียม

บทความนี้เผยแพร่ครั้งแรกใน Hackmamba