web-dev-qa-db-fra.com

Attendez que toutes les promesses soient résolues

J'ai donc une situation où j'ai plusieurs chaînes de promesses d'une longueur inconnue. Je veux une action à exécuter lorsque tous les chaînes ont été traitées. Est-ce que c'est possible? Voici un exemple:

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

Dans cet exemple, j'ai configuré une $q.all() pour les promesses un, deux et trois, qui seront résolues à un moment quelconque. J'ajoute ensuite des promesses aux extrémités un et trois. Je veux que le all soit résolu lorsque toutes les chaînes ont été résolues. Voici la sortie lorsque je lance ce code:

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

Y a-t-il un moyen d'attendre que les chaînes se résolvent?

106
jensengar

Je veux que tout soit résolu quand toutes les chaînes auront été résolues.

Bien sûr, passez simplement la promesse de chaque chaîne dans la all() au lieu des promesses initiales:

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

Le réponse acceptée est correct. Je voudrais donner un exemple pour l’élaborer un peu à ceux qui ne sont pas familiers avec promise.

Exemple:

Dans mon exemple, je dois remplacer les attributs src de img par des URL miroir différentes, le cas échéant, avant le rendu du contenu.

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

Explication:

De AngularJS docs :

La méthode then:

then (successCallback, errorCallback, notifyCallback) - quel que soit le moment où la promesse a été ou sera résolue ou rejetée, puis appelle l'un des rappels de succès ou d'erreur de manière asynchrone dès que le résultat est disponible. Les callbacks sont appelés avec un argument unique : le motif du résultat ou du rejet.

$ q.all (promesses)

Combine plusieurs promesses en une seule promesse qui est résolue lorsque toutes les promesses de saisie sont résolues.

Le paramètre promises peut être un tableau de promesses.

À propos de bind(), plus d'informations ici: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind

16
Hieu

Récemment eu ce problème, mais avec un nombre inconnu de promesses. Résolu en utilisant 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;
}
3
SoniCue
0
Nikola Yovchev

Vous pouvez utiliser "wait" dans une "fonction asynchrone" .

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

REMARQUE: je ne suis pas sûr à 100% que vous puissiez appeler une fonction asynchrone à partir d'une fonction non asynchrone et obtenir les bons résultats.

Cela dit, cela ne serait jamais utilisé sur un site Web. Mais pour le test de charge/test d'intégration ... peut-être.

Exemple de code:

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