2016-09-27 17 views
17

Mogę asynchronicznie rozwiązać kilka obietnic z Promise.all(array). Jednak .then() będzie działać dopiero po rozwiązaniu wszystkich tych obietnic. Jak mogę wykonywać działania, gdy obietnice zostaną rozwiązane?Wykonywanie działań zgodnie z obietnicami jest spełniane za pomocą Promise.all()

Na przykład chcę załadować wszystkie akapity z artykułu asynchronicznie za pomocą Promise.all() - W ten sposób sieć żąda natychmiast całego ognia. Jeśli ładuję się z paragrafem 1, chcę go renderować na stronie - ale tylko jeśli zostanie zrobione ładowanie przed akapitem 2, to chcę załadować akapit 2. Jeśli zrobi się paragraf 3, a 2 to nie, chcę 3 czekać na 2 przed renderowaniem na stronę. I tak dalej.

Próbowałem coś takiego, ale nie wiem, co robić dalej:

var getStuff = function(number, time){ 
    return new Promise(function(resolve, reject){ 
    window.setTimeout(function(){resolve(`${number} - Done.`)}, time); 
    }); 
}; 

Promise.all([ getStuff(1, 200), 
       getStuff(2, 100), 
       getStuff(3, 250), 
       getStuff(4, 200), 
       getStuff(5, 300), 
       getStuff(6, 250), 
       getStuff(7, 5000)]) 
.then(function(data){ 
    console.log(data); 
}); 

Jak mogę uzyskać dziennik konsoli danych zdarzyć się jeden po drugim - bez rozstrzygania każdej obietnicy z then() przed złożeniem kolejnego wniosku? Czy jest lepszy sposób to zrobić?

+0

Niektóre obiecujące biblioteki mają * wywołania zwrotne * postępu. – alex

+0

Nie można uzyskać takiego zachowania za pomocą natywnego es6? Bez dodawania kolejnej biblioteki do mojego projektu? – hackrnaut

+1

Dlaczego nie po prostu 'getStuff (...)' i 'then' render, dla każdej obietnicy? –

Odpowiedz

18

Nie można zrealizować tego zamówienia, używając Promise.all, ponieważ przed podjęciem jakichkolwiek działań czeka na rozwiązanie. Tworzenie indywidualnie obietnice i ogień ich wniosków równolegle:

// create promises and make parallel (concurrent) requests 
const s1 = getStuff(1, 200); 
const s2 = getStuff(2, 100); 
const s3 = getStuff(3, 250); 
// ... 

Następnie należy utworzyć łańcuch reakcji, w jaki sposób przetwarzać je (stuff1 przed stuff2, stuff2 przed stuff3 itp)

// create a chain of reaction order to the results of parallel promises 
s1 
    .then(console.log) // s1 resolved: log result 
    .then(() => s2) // chain s2 
    .then(console.log) // s2 resolved: log result 
    .then(() => s3) // chain s3 
    // ... 
    .then(() => {  // chain another function at at the end for when all promises resolved 
    // all promises resolved (all data was logged) 
    } 

Aby zareagować na obietnicę wyników w tej samej kolejności, zostały utworzone obietnice, można zmienić funkcję getStuff, aby dynamicznie łańcuchować reakcje przy użyciu Array.prototype.reduce:

var times = [200, 100, 250, 200, 300, 250, 5000]; 
 

 
var getStuff = function(time, index) { // swap the order of arguments so number is the index passed in from Array.map 
 
    return new Promise((resolve, reject) => { 
 
    window.setTimeout(() => { 
 
     resolve(`${index + 1} - Done.`); // use index + 1 because indexes start at 0 
 
    }, time); 
 
    }); 
 
}; 
 

 
times 
 
    // map each time to a promise (and number to the index of that time + 1) and fire of a request 
 
    .map(getStuff) 
 
    // dynamically build a reaction chain for the results of promises 
 
    .reduce((chain, promise) => { 
 
    return chain 
 
     .then(() => promise) 
 
     .then(console.log); 
 
    }, Promise.resolve()) 
 
    .then(() => { 
 
    // all promises resolved (all data was logged in order) 
 
    });

+1

Ahh, to ma sens! Zasadniczo, jak tylko zadzwonię do getStuffa, oni wykonają żądania sieciowe i jeśli zrobię te łańcuchy później, otrzymam pożądane zachowanie, ponieważ każda obietnica zostanie rozwiązana, zanim nazwa .then zostanie wywołana w łańcuchu? – hackrnaut

+2

Gdy tylko utworzysz Obietnicę, uruchomi twoją prośbę. Możesz zbudować łańcuch w dowolnym momencie, niezależnie od tego, czy obietnica zostanie rozwiązana, czy nie. Jeśli obietnica ustąpi przed utworzeniem łańcucha, po prostu utrzyma swoją wartość, dopóki nie wykonasz łańcucha. Jeśli najpierw wykonasz łańcuch, łańcuch zaczeka na otrzymanie tej obietnicy. To jest piękno obietnic. Zawsze są one asynchroniczne i zawsze możesz udawać, że masz już je przy kodowaniu. – nem035

2

wiem, że nie rodzimy, ale z Bluebird można użyć Promise.some (napełnić po count obietnice zostały spełnione) lub Promise.mapSeries (napełnić obietnic szeregowo) jakoś osiągnąć przepływ można oczekiwać. Odpowiedź

Bluebird API

6

nem035 jest na miejscu. Chcę zauważyć, że zazwyczaj w tym przypadku chcesz podjąć tę samą akcję, gdy żądanie się wydarzy i kolejną akcję, gdy wszystkie są gotowe.

Można użyć .all za to z .map:

Promise.all([ getStuff(1, 200), 
      getStuff(2, 100), 
      getStuff(3, 250), 
      getStuff(4, 200), 
      getStuff(5, 300), 
      getStuff(6, 250), 
      getStuff(7, 5000)] 
.map(request => request.then(v => { 
    console.log("Request done! Got," v); // or some action per request 
    return v; 
})).then(data => console.log(data)); 

Można szef to dalej z .map wykorzystując fakt, że używasz tej samej funkcji dla każdego żądania:

Promise.all([[1, 200], 
      [2, 100], 
      [3, 250], 
      [4, 200], 
      [5, 300], 
      [6, 250], 
      [7, 5000]]) 
.map((a, b) => getStuff(a, b)) 
.map(request => request.then(v => { 
    console.log("Request done! Got," v); // or some action per request 
    return v; 
})).then(data => console.log(data)); 

I dalej:

Promise.all([200, 100, 250, 200, 300, 250, 5000]) 
.map((a, i) => getStuff(a, i + 1)) 
.map(request => request.then(v => { 
    console.log("Request done! Got," v); // or some action per request 
    return v; 
})).then(data => console.log(data)); 

Albo z Bluebird:

const sideEffect = v => console.log("Got partial result", v)); 
const data = [200, 100, 250, 200, 300, 250, 5000]; 
Promise.map(data, (a, i) => getStuff(a, i + 1).tap(sideEffect)) 
     .then(data => console.log(data)); 

Oczywiście - należy po prostu naprawić swój backend, jest całkowicie nieuzasadnione zapytać klienta, aby wnioski o 7 różnych częściach danych - posiada backend wziąć zakresy.

0

Normalna obsługa: Możesz bezpiecznie używać Promise.all(). Wykonawcy obietnicy będą uruchamiani równolegle, a wyniki zostaną zwrócone w kolejności, w jakiej złożysz obietnice w tablicy obietnic. Od Ciebie zależy sortowanie ich tak, jak lubisz. Tak jak w poniższym fragmencie mamy pięć obietnic, które każdy losowo rozstrzygnie w ciągu 5 sekund. Niezależnie od czasu rozwiązania otrzymasz wyniki, gdy najnowsze rozwiązanie zostanie rozwiązane;

var promises = [ new Promise(v => setTimeout(_ => v("1st paragraph text"),~~(Math.random()*5000))), 
 
       new Promise(v => setTimeout(_ => v("2nd paragraph text"),~~(Math.random()*5000))), 
 
       new Promise(v => setTimeout(_ => v("3rd paragraph text"),~~(Math.random()*5000))), 
 
       new Promise(v => setTimeout(_ => v("4th paragraph text"),~~(Math.random()*5000))), 
 
       new Promise(v => setTimeout(_ => v("5th paragraph text"),~~(Math.random()*5000))), 
 
       ]; 
 
Promise.all(promises) 
 
     .then(result => console.log(result.reduce((p,c) => p + "\n" + c)));

co chcesz: Ale wtedy nie chcą czekać tak długo, jako ostatni wykończeń lecz chcą przetwarzać je w takiej kolejności, jak najszybciej dostają rozwiązany. Wtedy Array.prototype.reduce() jest twoim najlepszym przyjacielem tutaj. Takich jak

var promises = [ new Promise(v => setTimeout(_ => v("1st paragraph text"),~~(Math.random()*5000))), 
 
       new Promise(v => setTimeout(_ => v("2nd paragraph text"),~~(Math.random()*5000))), 
 
       new Promise(v => setTimeout(_ => v("3rd paragraph text"),~~(Math.random()*5000))), 
 
       new Promise(v => setTimeout(_ => v("4th paragraph text"),~~(Math.random()*5000))), 
 
       new Promise(v => setTimeout(_ => v("5th paragraph text"),~~(Math.random()*5000))) 
 
       ]; 
 
promises.reduce((p,c) => p.then(result => (console.log(result + "\n"),c))) 
 
     .then(result => (console.log(result + "\n")));

proszę uruchomić kod kilka razy, aby zobaczyć, jak zachowuje się kod. Tekst zostanie zaktualizowany JAK NAJSZYBCIEJ, gdy obietnica zostanie rozwiązana, ale tylko wtedy, gdy nadejdzie kolej. Więc jeśli 1 rozstrzygnie po 2, zobaczymy 1 i 2, aby pojawić się od razu w ich kolejności, ale nie będą czekać na trzecie, aby rozwiązać ...