Wacht tot alle beloften worden opgelost

Dus ik heb een situatie waarin ik meerdere belofteketens van onbekende lengte heb. Ik wil dat er een actie wordt uitgevoerd wanneer alle KETTINGEN zijn verwerkt. Is dat zelfs mogelijk? Hier is een voorbeeld:

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 dit voorbeeld heb ik een $q.all()ingesteld voor beloften één, twee en drie die op een willekeurig moment zullen worden opgelost. Ik voeg dan beloften toe aan de uiteinden van één en drie. Ik wil dat de allwordt opgelost wanneer alle ketens zijn opgelost. Hier is de uitvoer wanneer ik deze code uitvoer:

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

Is er een manier om te wachten tot de ketens zijn opgelost?


Antwoord 1, autoriteit 100%

Ik wil dat alles wordt opgelost als alle ketens zijn opgelost.

Natuurlijk, geef dan de belofte van elke keten door in de all()in plaats van de eerste beloften:

$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");
});

Antwoord 2, autoriteit 10%

Het geaccepteerde antwoordis correct. Ik wil graag een voorbeeld geven om het een beetje uit te werken voor degenen die niet bekend zijn met promise.

Voorbeeld:

In mijn voorbeeld moet ik de src-attributen van img-tags vervangen door andere mirror-URL’s, indien beschikbaar, voordat de inhoud wordt weergegeven.

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

Uitleg:

Van AngularJS docs:

De thenmethode:

then(successCallback, errorCallback, notificationCallback)– ongeacht wanneer de belofte was of zal worden opgelost of afgewezen, dan belt
een van de succesvolle of fout-callbacks asynchroon zodra de
resultaat beschikbaar is. De callbacks worden aangeroepen met een single
argument
: het resultaat of de reden van afwijzing.

$q.all(beloften)

Combineert meerdere beloften in één enkele belofte die wordt opgelost wanneer
alle invoerbeloftes zijn opgelost.

De parameter promiseskan een reeks beloften zijn.

Over bind(), Meer info hier: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind


Antwoord 3, autoriteit 2%

Onlangs dit probleem gehad, maar met onbekend aantal beloften. Opgelost met jQuery.map().

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

Antwoord 4

Er is een manier. $q.all(...

Je kunt de onderstaande dingen controleren:

http://jsfiddle.net/ThomasBurleson/QqKuk/

http://denisonluz.com/blog/index.php/2013/10/06/angularjs-returning-multiple-promises-at-once-with-q-all/


Antwoord 5

U kunt “await” gebruiken in een ” asynchrone functie”.

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

OPMERKING: ik weet niet 100% zeker of je een async-functie kunt aanroepen vanuit een niet-async-functie en de juiste resultaten hebt.

Dat gezegd hebbende, zou dit nooit op een website worden gebruikt. Maar voor load-testing/integratietest… misschien.

Voorbeeldcode:

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

Other episodes