Promise.all: ลำดับของค่าที่แก้ไขแล้ว


189

มองไปที่MDNดูเหมือนว่าvaluesผ่านไปยังการthen()เรียกกลับของ Promise.all มีค่าตามลำดับของสัญญา ตัวอย่างเช่น:

var somePromises = [1, 2, 3, 4, 5].map(Promise.resolve);
return Promise.all(somePromises).then(function(results) {
  console.log(results) //  is [1, 2, 3, 4, 5] the guaranteed result?
});

ใครสามารถอ้างข้อมูลจำเพาะที่ระบุว่าvaluesควรจะเรียงลำดับใด?

PS: การเรียกใช้โค้ดเช่นนั้นแสดงให้เห็นว่าสิ่งนี้ดูเหมือนจะเป็นจริงแม้ว่าจะไม่มีข้อพิสูจน์แน่นอน - มันอาจเป็นเรื่องบังเอิญ

คำตอบ:


274

ไม่นานเพื่อที่จะเก็บรักษาไว้

ต่อไปนี้สเปคที่คุณเชื่อมโยงกับPromise.all(iterable)ใช้เวลาiterable(นั่นคือวัตถุที่สนับสนุนIteratorอินเตอร์เฟซ) เป็นพารามิเตอร์และต่อมาในสายPerformPromiseAll( iterator, constructor, resultCapability)กับมันที่ลูปหลังมากกว่าใช้iterable ซึ่งหมายความว่าถ้าหากคุณสั่งให้มันผ่านไปแล้วจะถูกสั่งอย่างเคร่งครัดพวกเขาจะยังคงถูกสั่งซื้อเมื่อผ่านเข้ามาIteratorStep(iterator)
Promise.all()

การแก้ไขจะดำเนินการผ่านทางPromise.all() Resolveที่สัญญาที่ได้รับการแก้ไขแต่ละรายการมี[[Index]]สล็อตภายในซึ่งทำเครื่องหมายดัชนีของสัญญาในอินพุตต้นฉบับ


ทั้งหมดนี้หมายความว่าเอาต์พุตมีการเรียงลำดับอย่างเคร่งครัดเป็นอินพุตตราบใดที่อินพุตถูกสั่งอย่างเคร่งครัด (ตัวอย่างเช่นอาร์เรย์)

คุณสามารถเห็นสิ่งนี้ได้ในการกระทำที่ซอด้านล่าง (ES6):

// Used to display results
const write = msg => {
  document.body.appendChild(document.createElement('div')).innerHTML = msg;
};

// Different speed async operations
const slow = new Promise(resolve => {
  setTimeout(resolve, 200, 'slow');
});
const instant = 'instant';
const quick = new Promise(resolve => {
  setTimeout(resolve, 50, 'quick');
});

// The order is preserved regardless of what resolved first
Promise.all([slow, instant, quick]).then(responses => {
  responses.map(response => write(response));
});


1
iterable จะไม่ได้รับคำสั่งอย่างเคร่งครัด? ใด ๆ iterable คือ "ได้รับคำสั่งอย่างเคร่งครัด" ตามคำสั่งมันผลิตค่าใน.
เบนจามิน Gruenbaum

หมายเหตุ - Firefox เป็นเบราว์เซอร์เดียวที่ใช้มันในสัญญาอย่างถูกต้อง Chrome จะปัจจุบันthrowexcpetion ถ้าคุณผ่านการ iterable Promise.allไป นอกจากนี้ฉันไม่ได้ตระหนักถึงการใช้งานสัญญาของผู้ใช้ใด ๆ ที่สนับสนุนการส่งผ่าน iterables แม้ว่าหลายคนจะถกเถียงกันและตัดสินใจในเวลานั้น
Benjamin Gruenbaum

3
@BenjaminGruenbaum เป็นไปไม่ได้ที่จะมี iterable ที่สร้างคำสั่งที่แตกต่างกันสองคำเมื่อถูกทำซ้ำสองครั้ง? ตัวอย่างเช่นสำรับไพ่ที่สร้างไพ่ตามลำดับแบบสุ่มเมื่อมีการวนซ้ำ? ฉันไม่ทราบว่าคำสั่งที่ "เคร่งครัด" เป็นคำศัพท์ที่ถูกต้องที่นี่ แต่ไม่ใช่ทั้งหมด iterables มีคำสั่งคงที่ ดังนั้นฉันคิดว่ามันสมเหตุสมผลที่จะบอกว่าตัววนซ้ำเป็น "สั่งอย่างเคร่งครัด" (สมมติว่าเป็นคำที่ถูกต้อง) แต่การทำซ้ำนั้นไม่ใช่
JLRishe

3
@JLRishe ฉันเดาว่าคุณพูดถูกมันเป็นตัววนซ้ำที่สั่ง - มันทำไม่ได้
Benjamin Gruenbaum

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

49

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

อย่างไรก็ตามฉันอยากจะชี้ให้เห็นว่าคำสั่งซื้อจะถูกเก็บไว้ในฝั่งลูกค้าเท่านั้น!

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

นี่คือตัวอย่างที่แสดงให้เห็นถึงปัญหาโดยใช้หมดเวลา:

Promise.all

const myPromises = [
  new Promise((resolve) => setTimeout(() => {resolve('A (slow)'); console.log('A (slow)')}, 1000)),
  new Promise((resolve) => setTimeout(() => {resolve('B (slower)'); console.log('B (slower)')}, 2000)),
  new Promise((resolve) => setTimeout(() => {resolve('C (fast)'); console.log('C (fast)')}, 10))
];

Promise.all(myPromises).then(console.log)

ในรหัสที่ปรากฏข้างต้นสามสัญญา (A, B, C) Promise.allจะได้รับ สัญญาทั้งสามดำเนินการด้วยความเร็วที่แตกต่างกัน (C เป็นวิธีที่เร็วที่สุดและเป็น B ที่ช้าที่สุด) นั่นเป็นเหตุผลที่console.logงบของสัญญาปรากฏในคำสั่งนี้:

C (fast) 
A (slow)
B (slower)

ถ้าสัญญาเป็นการเรียก AJAX ส่วนแบ็คเอนด์ระยะไกลจะได้รับค่าเหล่านี้ตามลำดับนี้ แต่ในฝั่งไคลเอ็นต์Promise.allทำให้มั่นใจได้ว่าผลลัพธ์จะเรียงตามตำแหน่งดั้งเดิมของmyPromisesอาเรย์ นั่นเป็นเหตุผลที่ผลลัพธ์สุดท้ายคือ:

['A (slow)', 'B (slower)', 'C (fast)']

หากคุณต้องการรับประกันการดำเนินการตามสัญญาที่เกิดขึ้นจริงของคุณคุณจะต้องมีแนวคิดเช่นคิวสัญญา นี่คือตัวอย่างการใช้p-queue (ระวังคุณต้องห่อคำสัญญาทั้งหมดในฟังก์ชั่น):

คิวสัญญาต่อเนื่อง

const PQueue = require('p-queue');
const queue = new PQueue({concurrency: 1});

// Thunked Promises:
const myPromises = [
  () => new Promise((resolve) => setTimeout(() => {
    resolve('A (slow)');
    console.log('A (slow)');
  }, 1000)),
  () => new Promise((resolve) => setTimeout(() => {
    resolve('B (slower)');
    console.log('B (slower)');
  }, 2000)),
  () => new Promise((resolve) => setTimeout(() => {
    resolve('C (fast)');
    console.log('C (fast)');
  }, 10))
];

queue.addAll(myPromises).then(console.log);

ผลลัพธ์

A (slow)
B (slower)
C (fast)

['A (slow)', 'B (slower)', 'C (fast)']

2
คำตอบที่ดีโดยเฉพาะการใช้ PQueue
ironstein

ฉันต้องการ Queue Promise Queue แต่จะต้องทำอย่างไรถ้าฉันต้องทำจาก sql records ในเพื่อ? ในขณะที่ไม่มีทางเลือกใน ES2017 ของเรา ES2018?
stackdave

PQueue ช่วยฉันด้วย! ขอบคุณ! :)
podeig

28

ใช่ค่าในอยู่ในลำดับเดียวกับresultspromises

หนึ่งอาจอ้างถึงข้อมูลจำเพาะของ ES6Promise.allแม้ว่ามันจะค่อนข้างซับซ้อนเนื่องจากใช้ iterator api และตัวสร้างสัญญาทั่วไป อย่างไรก็ตามคุณจะสังเกตเห็นว่าการเรียกกลับตัวแก้ปัญหาแต่ละรายการมี[[index]]คุณลักษณะที่สร้างขึ้นในการทำซ้ำอาร์เรย์สัญญาและใช้สำหรับการตั้งค่าในอาร์เรย์ผลลัพธ์


แปลกประหลาดฉันเห็นวิดีโอ youtube วันนี้ที่บอกว่าลำดับผลลัพธ์จะถูกกำหนดโดยคนแรกที่แก้ไขแล้วอันดับที่สองจากนั้น ..... ฉันคิดว่าวิดีโอ OP ผิดหรือเปล่า?
Royi Namir

1
@RoyiNamir: เห็นได้ชัดว่าเขาเป็น
Bergi

@Ozil Wat? ลำดับการแก้ไขตามลำดับเวลาไม่สำคัญเมื่อสัญญาทั้งหมดปฏิบัติตาม ลำดับของค่าในอาร์เรย์ผลลัพธ์นั้นเหมือนกับในอาร์เรย์อินพุตของสัญญา หากไม่ใช่คุณควรเปลี่ยนไปใช้การปฏิบัติตามสัญญาที่เหมาะสม
Bergi
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.