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해야하는지 사양을 인용 할 수 있습니까 ?

추신 : 그런 코드를 실행하는 것은 이것이 사실이지만 증거는 아니지만 사실이었던 것으로 나타났습니다. 우연의 일치 일 수 있습니다.

답변:


274

순서가 유지 됩니다.

당신이 연결 사양에 따라, Promise.all(iterable)소요 iterable(인 개체를이 지원하는 Iterator인터페이스)를 매개 변수로 나중에 통화에 대한 PerformPromiseAll( iterator, constructor, resultCapability)그것으로, 이상 후자의 루프를 iterable사용하여 IteratorStep(iterator).
즉, 전달하는 iterable 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은 값을 생성하는 순서에 따라 "엄격하게 정렬"됩니다.
Benjamin Gruenbaum

참고-Firefox는 약속에 따라 iterable을 올바르게 구현하는 유일한 브라우저입니다. throwiterable을에 전달하면 Chrome에서 현재 예외를 처리 Promise.all합니다. 또한 많은 iterables에 대해 논의하고 당시에 결정했지만 현재 iterables 전달을 지원하는 userland promise 구현에 대해 알지 못합니다.
Benjamin Gruenbaum

3
@BenjaminGruenbaum 두 번 반복 할 때 두 가지 다른 주문을 생성하는 반복 가능을 가질 수 없습니까? 예를 들어, 반복 될 때 무작위 순서로 카드를 생성하는 카드 덱? "엄격하게 정렬 된"이 올바른 용어인지는 모르겠지만 모든 반복 가능 항목에 고정 된 순서가있는 것은 아닙니다. 따라서 반복자 가 "엄격한 순서"라고 가정하는 것이 합리적이라고 생각 하지만 반복 가능한 것은 아닙니다.
JLRishe

3
@ JLRishe 나는 당신이 옳은 것 같아요, 그것은 주문 된 반복자입니다-iterables는 아닙니다.
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

Sequential Promise Queue가 필요하지만 결과 SQL 레코드에서 수행해야하는 경우 어떻게해야합니까? 에 대한? ES2017 ES2018에는 대안이 없습니까?
stackdave

PQueue가 도와주었습니다! 감사합니다! :)
podeig

28

예,의 값 results은와 순서가 같습니다 promises.

ES6 사양을Promise.all 인용 할 수도 있지만 사용 된 반복자 API 및 일반 약속 생성자로 인해 다소 복잡합니다. 그러나 각 리졸버 콜백에는 [[index]]약속 배열 반복에서 생성되고 결과 배열의 값을 설정하는 데 사용되는 속성이 있습니다.


이상하게도 오늘 유튜브 비디오를 보았는데 출력 순서는 첫 번째 해결 한 사람에 의해 결정된 다음 두 번째로 결정됩니다.
Royi Namir

1
@RoyiNamir : 분명히 그는 그렇습니다.
Bergi

@ 오질 왓? 모든 약속이 이행 될 때 시간순으로 해결되는 순서는 절대 중요하지 않습니다. 결과 배열의 값 순서는 입력 약속 배열과 동일합니다. 그렇지 않은 경우 적절한 약속 구현으로 전환해야합니다.
Bergi
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.