Promise.all: Thứ tự các giá trị được giải quyết


187

Nhìn vào MDN, có vẻ như valuesthông qua cuộc then()gọi lại của Promise.all chứa các giá trị theo thứ tự các lời hứa. Ví dụ:

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?
});

Bất cứ ai có thể trích dẫn một thông số cụ thể theo thứ tự valuesnên được trong?

PS: Chạy mã như thế cho thấy điều này có vẻ đúng mặc dù điều đó tất nhiên không có bằng chứng - đó có thể là sự trùng hợp.

Câu trả lời:


270

Một thời gian ngắn, trật tự được bảo tồn .

Theo thông số kỹ thuật mà bạn đã liên kết đến, Promise.all(iterable)lấy một iterable(đó là một đối tượng hỗ trợ Iteratorgiao diện) làm tham số và sau đó trong các cuộc gọi PerformPromiseAll( iterator, constructor, resultCapability)với nó, trong đó các vòng lặp sau iterablesử dụng IteratorStep(iterator).
Điều này có nghĩa là nếu lặp đi lặp lại mà bạn chuyển đến Promise.all()được yêu cầu nghiêm ngặt, họ vẫn sẽ được yêu cầu một khi được thông qua.

Việc giải quyết được thực hiện thông qua Promise.all() Resolvenơi mỗi lời hứa được giải quyết có một [[Index]]vị trí bên trong , đánh dấu chỉ số của lời hứa trong đầu vào ban đầu.


Tất cả điều này có nghĩa là đầu ra được sắp xếp đúng theo đầu vào miễn là đầu vào được sắp xếp đúng (ví dụ: một mảng).

Bạn có thể thấy điều này trong hành động trong fiddle dưới đây (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
Làm thế nào một iterable không được ra lệnh nghiêm ngặt? Bất kỳ phép lặp nào cũng được "sắp xếp nghiêm ngặt" theo thứ tự mà nó tạo ra các giá trị của nó.
Benjamin Gruenbaum

Lưu ý - Firefox là trình duyệt duy nhất thực hiện các lần lặp chính xác trong các lời hứa. Chrome hiện sẽ throwlà một đoạn trích nếu bạn vượt qua được Promise.all. Ngoài ra, tôi không biết về bất kỳ triển khai lời hứa nào của người dùng hiện đang hỗ trợ chuyển các vòng lặp mặc dù nhiều người đã tranh luận về nó và quyết định chống lại nó vào thời điểm đó.
Benjamin Gruenbaum

3
@BenjaminGruenbaum Không thể có một vòng lặp tạo ra hai đơn đặt hàng khác nhau khi được lặp lại hai lần? Ví dụ, một cỗ bài tạo ra các thẻ theo thứ tự ngẫu nhiên khi nó được lặp lại? Tôi không biết "trật tự nghiêm ngặt" có phải là thuật ngữ đúng ở đây không, nhưng không phải tất cả các lần lặp đều có một trật tự cố định. Vì vậy, tôi nghĩ thật hợp lý khi nói rằng các trình vòng lặp được "ra lệnh nghiêm ngặt" (giả sử đó là thuật ngữ đúng), nhưng các lần lặp thì không.
JLRishe

3
@JLRishe Tôi đoán bạn đúng, đó thực sự là các trình vòng lặp được ra lệnh - iterables thì không.
Benjamin Gruenbaum

8
Điều đáng chú ý là những lời hứa không có chuỗi. Mặc dù bạn sẽ nhận được độ phân giải theo cùng một thứ tự, không có gì đảm bảo về thời điểm các lời hứa được thực hiện. Nói cách khác, Promise.allkhông thể được sử dụng để chạy một loạt các lời hứa theo thứ tự, từng cái một. Các lời hứa được nạp vào iterator cần phải độc lập với nhau để điều này có thể dự đoán được.
Andrew Eddie

46

Như các câu trả lời trước đã nêu, Promise.alltổng hợp tất cả các giá trị được giải quyết với một mảng tương ứng với thứ tự đầu vào của Lời hứa ban đầu (xem Lời hứa tổng hợp ).

Tuy nhiên, tôi muốn chỉ ra rằng, đơn hàng chỉ được bảo tồn ở phía khách hàng!

Đối với nhà phát triển, có vẻ như Lời hứa đã được thực hiện theo thứ tự nhưng trên thực tế, Lời hứa được xử lý ở các tốc độ khác nhau. Điều này rất quan trọng để biết khi nào bạn làm việc với một phụ trợ từ xa vì phụ trợ có thể nhận được Lời hứa của bạn theo một thứ tự khác.

Dưới đây là một ví dụ minh họa vấn đề bằng cách sử dụng thời gian chờ:

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)

Trong mã được hiển thị ở trên, ba Lời hứa (A, B, C) được đưa ra Promise.all. Ba Promise thực hiện ở các tốc độ khác nhau (C là nhanh nhất và B là chậm nhất). Đó là lý do tại sao các console.logtuyên bố của Lời hứa hiển thị theo thứ tự này:

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

Nếu Lời hứa là các cuộc gọi AJAX, thì một phụ trợ từ xa sẽ nhận được các giá trị này theo thứ tự này. Nhưng về phía khách hàng Promise.allđảm bảo rằng các kết quả được sắp xếp theo vị trí ban đầu của myPromisesmảng. Đó là lý do tại sao kết quả cuối cùng là:

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

Nếu bạn muốn đảm bảo thực hiện đúng các Lời hứa của mình, thì bạn sẽ cần một khái niệm như hàng đợi Promise. Dưới đây là một ví dụ sử dụng hàng đợi p (hãy cẩn thận, bạn cần phải bọc tất cả các Lời hứa trong các chức năng):

Hàng đợi hứa hẹn tuần tự

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);

Kết quả

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

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

2
câu trả lời tuyệt vời, đặc biệt là sử dụng PQueue
ironstein

Tôi cần một hàng đợi hứa hẹn tuần tự, nhưng làm thế nào nếu tôi phải làm điều đó từ một bản ghi sql kết quả? trong một cho? trong khi?, không có sự thay thế nào trong ES2017 ES2018 của chúng tôi?
stackdave

27

Có, các giá trị theo resultsthứ tự giống như promises.

Người ta có thể trích dẫn thông số ES6Promise.all , mặc dù nó hơi phức tạp do trình tạo api lặp và sử dụng lời hứa chung chung. Tuy nhiên, bạn sẽ nhận thấy rằng mỗi cuộc gọi lại trình phân giải có một [[index]]thuộc tính được tạo trong phép lặp mảng hứa hẹn và được sử dụng để đặt các giá trị trên mảng kết quả.


Thật kỳ lạ, tôi đã xem một video youtube ngày hôm nay nói rằng thứ tự đầu ra được xác định bởi người đầu tiên đã giải quyết, sau đó thứ hai, sau đó ..... Tôi đoán rằng video OP đã sai?
Royi Namir

1
@RoyiNamir: Rõ ràng là anh ấy.
Bergi

@Ozil Wat? Thứ tự thời gian của độ phân giải hoàn toàn không quan trọng khi tất cả các lời hứa được thực hiện. Thứ tự của các giá trị trong mảng kết quả giống như trong mảng đầu vào của các lời hứa. Nếu không, bạn nên chuyển sang thực hiện lời hứa thích hợp.
Bergi
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.