Promise.all: Çözülen değerlerin sırası


190

MDN'ye baktığımızda Promise.all'ın geri aramasına valuesaktarılmış gibi görünüyor then(), tüm değerleri sözlerin sırasına göre içeriyor. Örneğin:

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

Herkes hangi sırayla valuesolması gerektiğini belirten bir özellik teklif edebilir ?

Not: Bu şekilde çalışan kod, elbette bir kanıt olmamasına rağmen bunun doğru olduğunu gösterdi - tesadüf olabilirdi.

Yanıtlar:


274

Kısaca, sipariş korunur .

Eğer bağlı spec sonra, Promise.all(iterable)bir alır iterable(olup, bir nesne olduğu destekler Iteratorarayüzü), bir parametre olarak ve daha sonra çağrılarda PerformPromiseAll( iterator, constructor, resultCapability)onunla üzerinde ikinci döngüleri iterablekullanarak IteratorStep(iterator).
Bu, geçtiğiniz yinelenebilir Promise.all()kesinlikle sipariş edilirse, bir kez geçtikten sonra sipariş verilecekleri anlamına gelir .

Çözümleme, Promise.all() Resolveçözülen her sözün, [[Index]]orijinal girdideki sözün dizinini işaretleyen dahili bir yuvaya sahip olduğu durumlarda uygulanır .


Bütün bunlar, giriş kesinlikle sipariş edildiği sürece (örneğin, bir dizi) girişin kesin olarak sıralandığı anlamına gelir.

Bunu aşağıdaki kemanda (ES6) çalışırken görebilirsiniz:

// 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
Nasıl bir tekrarlanabilir kesinlikle sipariş değil? Herhangi bir yinelenebilir değerlerini ürettiği sıraya göre "kesinlikle sipariş edilir"
Benjamin Gruenbaum

Not - Firefox, vaatlerde yinelenenleri doğru şekilde uygulayan tek tarayıcıdır. Chrome, throwbir yinelenebilir öğeyi geçerseniz şu anda bir heyecan olacaktır Promise.all. Buna ek olarak, birçoğu tartışmasına ve o zamana karşı karar vermesine rağmen, şu anda yinelenebilir geçişleri destekleyen herhangi bir kullanıcı vaat uygulamasının farkında değilim.
Benjamin Gruenbaum

3
@BenjaminGruenbaum İki kez tekrarlandıktan sonra iki farklı sipariş üreten bir yinelemeye sahip olmak mümkün değil mi? Örneğin, tekrarlandığında kartları rastgele sırada üreten bir deste destesi? "Kesinlikle sipariş edilmiş" burada doğru terminoloji olup olmadığını bilmiyorum, ancak tüm yinelenebilirler sabit bir sıraya sahip değil. Bu yüzden yineleyicilerin "kesinlikle sipariş edildiğini" (doğru terim olduğunu varsayarak) söylemenin makul olduğunu düşünüyorum , ancak yinelenebilir değil.
JLRishe

3
@JLRishe Haklısın, sipariş edilen yineleyiciler - yinelenebilir değil.
Benjamin Gruenbaum

8
Sözlerin zincirlenmediğini belirtmek gerekir. Çözünürlüğü aynı sırayla alırken, sözlerin ne zaman yapılacağı konusunda hiçbir garanti yoktur. Diğer bir deyişle, Promise.allbir dizi sözü arka arkaya sıralamak için kullanılamaz. Yineleyiciye yüklenen vaatlerin öngörülebilir şekilde çalışması için birbirinden bağımsız olması gerekir.
Andrew Eddie

49

Önceki yanıtların daha önce belirttiği gibi, Promise.alltüm çözümlenmiş değerleri, orijinal Vaatlerin giriş sırasına karşılık gelen bir dizi ile toplar (bkz . Vaatleri Birleştirme ).

Ancak, siparişin yalnızca müşteri tarafında korunduğunu belirtmek isterim!

Geliştirici için, Sözler sırayla yerine getirilmiş gibi görünüyor, ancak gerçekte, Sözler farklı hızlarda işleniyor. Uzak bir arka uçla çalıştığınızda bunu bilmek önemlidir, çünkü arka uç sözlerinizi farklı bir sırayla alabilir.

Zaman aşımlarını kullanarak sorunu gösteren bir örnek:

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)

Yukarıda gösterilen kodda üç Vaat (A, B, C) verilmiştir Promise.all. Üç Vaat farklı hızlarda gerçekleşir (C en hızlı ve B en yavaştır). Bu yüzden console.logVaatlerin ifadeleri şu sırayla ortaya çıkıyor:

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

Sözler AJAX çağrılarıysa, uzak bir arka uç bu değerleri bu sırayla alır. Ancak istemci tarafında Promise.all, sonuçların myPromisesdizinin orijinal konumlarına göre sıralanmasını sağlar . Bu yüzden nihai sonuç:

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

Sözlerinizin gerçek olarak yürütülmesini de garanti etmek istiyorsanız, bir Söz kuyruğu gibi bir konsepte ihtiyacınız olacaktır. İşte p-queue kullanan bir örnek (dikkatli olun, tüm vaatleri fonksiyonlara sarmanız gerekir):

Sıralı Promise Kuyruğu

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

Sonuç

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

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

2
büyük cevap, expecially PQueue kullanarak
ironstein

Ben bir Sıralı Promise Kuyruğu gerekir, ama bir sonuç sql kayıtlarından bunu yapmak gerekir nasıl? için bir? ES2017'de bizim ES2018'de alternatif yok mu?
stackdave

PQueue bana yardımcı oldu! Teşekkür ederim! :)
podeig

28

Evet, içindeki değerler ile resultsaynı sırada promises.

Bir alıntı olabilir üzerinde ES6 specPromise.all o kullanılan yineleyici API ve jenerik söz yapıcı nedeniyle biraz kıvrık olsa. Ancak, her çözümleyici geri çağrısının [[index]]promise-dizi yinelemesinde oluşturulan ve sonuç dizisindeki değerleri ayarlamak için kullanılan bir özniteliği olduğunu fark edeceksiniz .


Garip, ben çıkış sırası karar ilk, daha sonra ikinci karar verdiğini belirten bir youtube video bugün gördüm ..... Sanırım video OP yanlış mı?
Royi Namir

1
@RoyiNamir: Görünüşe göre öyleydi.
Bergi

@Ozil Wat? Tüm vaatlerin ne zaman yerine getirildiği kronolojik çözüm sırası kesinlikle önemli değildir. Sonuç dizisindeki değerlerin sırası, vaatlerin giriş dizisiyle aynıdır. Değilse, uygun bir söz uygulamasına geçmelisiniz.
Bergi
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.