2014-02-13 8 views
98

So habe ich eine Situation, wo ich mehrere Versprechen Ketten einer unbekannten Länge haben. Ich möchte eine Aktion ausführen, wenn alle Ketten verarbeitet wurden. Ist das überhaupt möglich? Hier ein Beispiel:Warte auf alle Versprechungen zu lösen

app.controller('MainCtrl', function($scope, $q, $timeout) { 
    var one = $q.defer(); 
    var two = $q.defer(); 
    var three = $q.defer(); 

    var all = $q.all([one.promise, two.promise, three.promise]); 
    all.then(allSuccess); 

    function success(data) { 
     console.log(data); 
     return data + "Chained"; 
    } 

    function allSuccess(){ 
     console.log("ALL PROMISES RESOLVED") 
    } 

    one.promise.then(success).then(success); 
    two.promise.then(success); 
    three.promise.then(success).then(success).then(success); 

    $timeout(function() { 
     one.resolve("one done"); 
    }, Math.random() * 1000); 

    $timeout(function() { 
     two.resolve("two done"); 
    }, Math.random() * 1000); 

    $timeout(function() { 
     three.resolve("three done"); 
    }, Math.random() * 1000); 
}); 

In diesem Beispiel stellte ich einen $q.all() für Versprechungen ein, zwei und drei, die an einigen zufälligen Zeit gelöst bekommen wird. Ich füge dann Versprechen zu den Enden von eins und drei hinzu. Ich möchte die all auflösen, wenn alle Ketten aufgelöst worden sind. Hier ist die Ausgabe, wenn ich diesen Code ausführen:

one done 
one doneChained 
two done 
three done 
ALL PROMISES RESOLVED 
three doneChained 
three doneChainedChained 

Gibt es eine Möglichkeit zu warten, bis die Ketten zu lösen?

Antwort

142

ich die alle wollen, zu lösen, wenn alle Ketten wurden gelöst.

Sicher, dann übergeben Sie einfach das Versprechen jeder Kette in die all() anstelle der ursprünglichen Versprechen:

$q.all([one.promise, two.promise, three.promise]).then(function() { 
    console.log("ALL INITIAL PROMISES RESOLVED"); 
}); 

var onechain = one.promise.then(success).then(success), 
    twochain = two.promise.then(success), 
    threechain = three.promise.then(success).then(success).then(success); 

$q.all([onechain, twochain, threechain]).then(function() { 
    console.log("ALL PROMISES RESOLVED"); 
}); 
+2

Danke für die Bestätigung meiner schlimmsten Angst. Jetzt muss ich einen Weg finden, um das letzte Versprechen zu bekommen, lol. –

+0

Was ist das Problem damit? Sind Ihre Ketten dynamisch aufgebaut? – Bergi

+0

Genau mein Problem. Ich versuche dynamisch eine Versprechenskette zu erstellen, dann möchte ich etwas tun, wenn die Kette (n) fertig sind. –

15

Die accepted answer korrekt ist. Ich möchte ein Beispiel geben, um es ein wenig zu denen zu erarbeiten, die nicht mit promise vertraut sind.

Beispiel:

In meinem Beispiel, ich brauche die src Attribute img Tags mit verschiedenen Spiegeln Urls falls vorhanden ersetzen, bevor Sie den Inhalt zu machen.

var img_tags = content.querySelectorAll('img'); 

function checkMirrorAvailability(url) { 

    // blah blah 

    return promise; 
} 

function changeSrc(success, y, response) { 
    if (success === true) { 
     img_tags[y].setAttribute('src', response.mirror_url); 
    } 
    else { 
     console.log('No mirrors for: ' + img_tags[y].getAttribute('src')); 
    } 
} 

var promise_array = []; 

for (var y = 0; y < img_tags.length; y++) { 
    var img_src = img_tags[y].getAttribute('src'); 

    promise_array.push(
     checkMirrorAvailability(img_src) 
     .then(

      // a callback function only accept ONE argument. 
      // Here, we use `.bind` to pass additional arguments to the 
      // callback function (changeSrc). 

      // successCallback 
      changeSrc.bind(null, true, y), 
      // errorCallback 
      changeSrc.bind(null, false, y) 
     ) 
    ); 
} 

$q.all(promise_array) 
.then(
    function() { 
     console.log('all promises have returned with either success or failure!'); 
     render(content); 
    } 
    // We don't need an errorCallback function here, because above we handled 
    // all errors. 
); 

Erläuterung:

Von AngularJS docs:

Die then Methode:

dann (successCallback, errorCallback, notifyCallback) - unabhängig davon, wann das Versprechen war oder wird aufgelöst oder abgelehnt, dann ruft 012 aufeiner der Erfolgs- oder Fehlerrückrufe asynchron, sobald das Ergebnis verfügbar ist. Die Callbacks werden mit einem einzigen Argument aufgerufen: der Grund für das Ergebnis oder die Ablehnung.

$ q.all (Versprechen)

Kombiniert mehrere Versprechungen in ein einziges Versprechen, das gelöst wird, wenn alle Eingangs Versprechen gelöst werden.

Der promises Parameter kann eine Reihe von Versprechen sein.

über bind(), Mehr Infos hier: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind

+0

Die 'then'-Methode von' $ q.all' ist ein Array der zurückgegebenen Versprechungen, so dass Sie dieses Array loopen und 'then' für jedes Element im Array aufrufen können, im Gegensatz zum Aufruf von' then', wenn Sie hinzufügen das Versprechen, 'promise_array' zu versprechen. – nick

3

Vor kurzem hatten dieses Problem aber mit unbekannter Anzahl von promises.Solved jQuery.map() verwenden.

function methodThatChainsPromises(args) { 

    //var args = [ 
    // 'myArg1', 
    // 'myArg2', 
    // 'myArg3', 
    //]; 

    var deferred = $q.defer(); 
    var chain = args.map(methodThatTakeArgAndReturnsPromise); 

    $q.all(chain) 
    .then(function() { 
     $log.debug('All promises have been resolved.'); 
     deferred.resolve(); 
    }) 
    .catch(function() { 
     $log.debug('One or more promises failed.'); 
     deferred.reject(); 
    }); 

    return deferred.promise; 
} 
+1

Ich sehe keine Verwendung von jQuery in Ihrem Code? – Bergi

+0

Es ist nicht jQuery.map(), sondern Array.prototype.map() (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map), aber dieser Ansatz funktioniert. – Anastasia

0

Sie können "await" in an "async function" verwenden.

app.controller('MainCtrl', async function($scope, $q, $timeout) { 
    ... 
    var all = await $q.all([one.promise, two.promise, three.promise]); 
    ... 
} 

HINWEIS: Ich kann nicht 100% sicher bin, Sie eine Asynchron-Funktion aus einer nicht-Asynchron-Funktion aufrufen können und die richtigen Ergebnisse.

Das sagte, das würde nie auf einer Website verwendet werden. Aber zum Belastungstest/Integrationstest ... vielleicht.

Beispielcode:

async function waitForIt(printMe) { 
 
    console.log(printMe); 
 
    console.log("..."+await req()); 
 
    console.log("Legendary!") 
 
} 
 

 
function req() { 
 
    
 
    var promise = new Promise(resolve => { 
 
    setTimeout(() => { 
 
     resolve("DARY!"); 
 
    }, 2000); 
 
    
 
    }); 
 

 
    return promise; 
 
} 
 

 
waitForIt("Legen-Wait For It");

Verwandte Themen