2016-12-21 1 views
0

Mit einem Array von Promises wie kann ich warten zu lösen, bis sie vor dem Fortfahren alle gelöst haben?Warten auf Promises in einem Array

Unten ist eine Beispielimplementierung, wo task_2 feuert seine Ablehnung vor task_1 auslösen kann.

const $output = $$('output') 
 

 
const config = { 
 
    task_1: [ 
 
    val => new Promise((resolve, reject) => setTimeout(() => reject('Awaited error'), 2000)), 
 
    val => new Promise((resolve, reject) => resolve(val)) 
 
    ], 
 
    task_2: [ 
 
    val => new Promise((resolve, reject) => resolve(val)), 
 
    val => new Promise((resolve, reject) => reject('An error')), 
 
    ] 
 
} 
 

 
taskRunner({ 
 
    task_1: 'task parameters', 
 
    task_2: 'task parameters' 
 
    }) 
 
    .then(res => $output.text(res).fadeIn()) 
 
    .catch(err => $output.text(err).fadeIn()) 
 

 
function taskRunner(tasks) { 
 
    return new Promise((resolve, reject) => { 
 
    let arr = [] 
 
    for (const task in tasks) { 
 
     if (!config.hasOwnProperty(task)) return reject(`${task} has no tasks`) 
 
     arr.push(config[task].map((cb => cb(tasks[task])))) 
 
    } 
 

 
    const toRun = arr.reduce((a, b) => a.concat(b)) 
 
    Promise.all(toRun) 
 
     .then(res => resolve(res)) 
 
     .catch(err => reject(err)) 
 
    }) 
 
} 
 

 
function $$(data) { 
 
    return $('[data-' + data + ']') 
 
}
html, body { margin: 0; padding: 0 } 
 
div.output { 
 
    font-family: 'Courier', monospace; 
 
    color: #383838; 
 
    margin: 15px; 
 
    padding: 15px 20px; 
 
    background: #fafafa; 
 
    border-radius: 3px; 
 
    border-bottom: 1px solid #dadada; 
 
} 
 
.js-hide { display: none }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> 
 
<div class="output js-hide" data-output></div>

+1

Was ist die Frage? Was verhält sich nicht wie erwartet? Es ist wirklich nicht notwendig, ein neues Versprechen in Ihrem Taskrunner zu erstellen, Sie können einfach 'Promise.all (..)' zurückgeben. –

+0

Gemäß der Dokumentation Versprechen Alle wird abgelehnt, wenn eines der Elemente abgelehnt wird: https: // Entwickler. Mozilla.org/de-DE/docs/Web/JavaScript/Referenz/Global_Objects/Promise/all –

+0

@DanielB Die erste Aufgabe wird nicht zuerst abgelehnt, zweitens, wie könnte ich die Ablehnung einer Aufgabe, die nicht in der 'config' existiert, nur mit umgehen Rückgabe von 'promise.all()'? – MindVox

Antwort

0

Wenn ich Ihre Frage richtig aus Ihrem Kommentar verstehen

Es ist eigentlich nur ein Beispiel, ich bin gespannt, wie Sie für eine Reihe von Versprechungen zu Entschlossenheit warten würden/Ablehnen, bevor Sie fortfahren. Sie könnten zum Beispiel eine Reihe von Promises haben, die HTTP-Aufrufe durchführen. Im Fehlerfall möchten Sie lieber alle Fehler als die neuesten ausgeben.

Sie möchten warten, bis alle Versprechen entweder gelöst oder abgelehnt wurden, und dann fortfahren.

Promise.all() wird abgelehnt, wenn einer der gegebenen Versprechen im Array abgelehnt wird, daher ist es in diesem Fall keine Alternative für Sie.

Es gibt eine .always() Funktion in jQuery, die so arbeiten, und wenn Sie BlueBird verwenden sind, können Sie .reflect() verwenden, um die Versprechungen der Promise.all() zu inspizieren. Es gibt keine aktuelle Funktion in ES6, die dies tun kann.

aus der Antwort auf diese Frage Leihen, ES6 promise settled callback?, können Sie Ihre eigene Version von .always()/.finally()/.allSettled() wie diese

Promise.prototype.finally = function(cb) { 
    const res =() => this 
    return this.then(value => 
     Promise.resolve(cb({state:"fulfilled", value})).then(res) 
    , reason => 
     Promise.resolve(cb({state:"rejected", reason})).then(res) 
    ); 
}; 
0

implementieren Wenn ich Ihr Problem richtig verstehe ich denke man noch Promise.all() verwenden könnten aber Sie Sie müssen Ihre Versprechen durch eine Funktion einpacken, um die Ablehnungen getrennt zu behandeln. Ich meine, Sie können die Ablehnung fangen, bevor es einen vorzeitigen Ausgang des Promise.all() auslöst und die Zurückweisung behandelt, indem Sie es mit einem speziellen Gegenstand auflösen.

Als ein Beispiel;

function handleRejectedPromises(p){ 
 
    return new Promise((resolve,reject) => p.then(v => resolve({status: "resolved", value: v})) 
 
              .catch(x => resolve({status: "rejected", reason: x}))); 
 
} 
 
var ps = [Promise.resolve(42), Promise.reject(37), Promise.resolve(23), Promise.resolve(11)]; 
 

 
Promise.all(ps.map(p => handleRejectedPromises(p))) 
 
     .then(ra => ra.forEach(r => console.log(r)));

OK per @ i Bergi Kommentar habe meine Antwort entsprechend, um es auszuziehen Bereich der anti-Muster zu korrigieren.

function handleRejectedPromises(p){ 
 
    return p.then(v => ({status: "resolved", value: v})) 
 
      .catch(x => ({status: "rejected", reason: x})); 
 
} 
 
var ps = [Promise.resolve(42), Promise.reject(37), Promise.resolve(23), Promise.resolve(11)]; 
 

 
Promise.all(ps.map(p => handleRejectedPromises(p))) 
 
     .then(ra => ra.forEach(r => console.log(r)));

+0

Vermeiden Sie die ['Promise' Konstruktor Antipattern] (http://stackoverflow.com/q/23803743/1048572? Was -ist- die- Promise-Construction-Antipattern-and-How-to-Avoid-It)! – Bergi

+0

@Bergi Ich stimme zu, ich möchte auch nicht versprechen, Bau innerhalb der Versprechen noch zu tragen ... Nein, ich glaube nicht, dass dies ein Anti-Muster ist, wie in diesem Thema betroffen ist. Ich glaube, dies ist die Art und Weise, wie diese Aufgabe innerhalb der verfügbaren Grenzen von nativen JS-Versprechungen erfüllt werden muss. Manchmal gibt es Dinge, die man zu erfinden hat, die unorthodox erscheinen mögen, wie Umkehrung von Entschlossenheit und Zurückweisung in "Promise.all()", um das erste Versprechen zu bekommen, ohne dass man sich an die ablehnenden Versprechen verfängt. Dies ist nur einer von ihnen. – Redu

+1

Es ist definitiv ein Antipattern. Das ganze 'neue Promise'-Konstrukt sollte einfach durch das Äquivalent (sans your bugs) ersetzt werden und viel einfacher' p.then (v => ({status: "resolved", value: v}), x => ({status : "abgelehnt", Grund: x})) '. – Bergi