web-dev-qa-db-fra.com

Chaînage dynamique dans les promesses Javascript

Comment puis-je effectuer des chaînages dynamiques dans les promesses Javascript, je n'ai toujours vu que le codage en dur des appels, par exemple, (promise).then(request/functionName).then(request/functionName)

16
Ramu Ajay

Une option consiste à utiliser les propriétés des objets et la possibilité de les invoquer via des chaînes.

J'ai écrit un petit échantillon ici et l'ai posté ci-dessous.

L'idée est que vous avez l'ensemble des fonctions que vous souhaitez exécuter définies dans un espace de noms ou un objet, comme je l'ai fait dans 'myNamespace':

myNamespace = {
    "A": function() {return "A Function";},
    "B": function() {return "B Function";},
    "C": function() {return "C Function";}
}

Ensuite, votre promesse principale sera exécutée et d’une manière ou d’une autre (via les entrées, ajax, invites, etc.), vous obtiendrez la valeur de chaîne de la fonction que vous souhaitez exécuter, qui n’est pas connue au moment de l’exécution:

Ma principale promesse utilise une invite pour recevoir une lettre de l'utilisateur:

var answer = Prompt('Starting.  Please pick a letter: A,B,C');
        if(myNamespace[answer] === undefined)
        {
            alert("Invalid choice!");
            reject("Invalid choice of: " + answer);
        }
        else
        {
            resolve(answer);
        }

Dans le prochain "alors", j'utilise cette valeur (transmise via la fonction de résolution) pour appeler la fonction:

.then(function(response) {
        funcToRun = myNamespace[response]();})

Enfin, je produis en HTML le résultat de mon appel de fonction dynamique et j’utilise un certain plaisir récursif pour le rendre plus interactif et démontrer qu’il est dynamique:

.then(function(){
        document.getElementById('result').innerHTML = funcToRun;})
    .then(function(){
        if(Prompt("Run Again? (YES/NO)")==="YES")
        {
            doWork();
        }
    });

myNamespace = {
    "A": function() {return "A Function";},
    "B": function() {return "B Function";},
    "C": function() {return "C Function";}
}

function doWork()
{
    var funcToRun;
    
    new Promise(function(resolve,reject) {
        var answer = Prompt('Starting.  Please pick a letter: A,B,C');
        if(myNamespace[answer] === undefined)
        {
            alert("Invalid choice!");
            reject("Invalid choice of: " + answer);
        }
        else
        {
            resolve(answer);
        }
    })
    .then(function(response) {
        funcToRun = myNamespace[response]();})
    .then(function(){
        document.getElementById('result').innerHTML = funcToRun;})
    .then(function(){
        if(Prompt("Run Again? (YES/NO)")==="YES")
        {
            doWork();
        }
    });
}

doWork();
<div id="result"></div>

6
JasonWilczak

Étant donné les fonctions d'un tableau qui renvoient toutes des promesses, vous pouvez utiliser reduce() pour les exécuter en séquence:

var myAsyncFuncs = [
    function (val) {return Promise.resolve(val + 1);},
    function (val) {return Promise.resolve(val + 2);},
    function (val) {return Promise.resolve(val + 3);},
];

myAsyncFuncs.reduce(function (prev, curr) {
    return prev.then(curr);
}, Promise.resolve(1))
.then(function (result) {
    console.log('RESULT is ' + result);  // prints "RESULT is 7"
});

L'exemple ci-dessus utilise ES6 Promises mais toutes les bibliothèques de promesses ont des fonctionnalités similaires.

En outre, la création du tableau de fonctions de retour de promesse est généralement un bon candidat pour utiliser map(). Par exemple:

myNewOrmModels.map(function (model) {
    return model.save.bind(model);
}).reduce(function (prev, curr) {
    return prev.then(curr);
}, Promise.resolve())
.then(function (result) {
    console.log('DONE saving');
});
36
wuher

Puisque les promesses se défont, continuez simplement d’ajouter des déclarations then et elles continueront d’être enchaînées.

function asyncSeries(fns) {
  return fns.reduce(function(p, fn) {
    return p.then(fn);
  }, Promise.resolve());
}

Récursivement, c’est une façon plutôt cool de le faire aussi :)

function countTo(n, sleepTime) {
  return _count(1);

  function _count(current) {
    if (current > n) {
      return Promise.resolve();
    }

    return new Promise(function(resolve, reject) {
      console.info(current);
      setTimeout(function() {
        resolve(_count(current + 1));
      }, sleepTime);
    });
  }
}
5
Travis Kaufman

Ceci est la façon ES7.

Disons que vous avez plusieurs promesses définies dans un tableau.

  var funcs = [
    _ => new Promise(res => setTimeout(_ => res("1"), 1000)),
    _ => new Promise(res => setTimeout(_ => res("2"), 1000))
  }

Et vous voulez appeler comme ça.

 chainPromises(funcs).then(result => console.log(result));

Vous pouvez utiliser async et await à cette fin.

  async function chainPromises(promises) {
    for (let promise of promises) {  // must be for (.. of ..)
      await promise();
    }
  }

Ceci exécutera les fonctions données séquentiellement (une par une), pas en parallèle. Le paramètre promises est un tableau de fonctions qui retournent Promise.

Plunker: http://plnkr.co/edit/UP0rhD?p=preview

1
allenhwkim

Je pense que le moyen le plus simple est:

const executePromises = function(listOfProviders){

    const p = Promise.resolve(null);

    for(let i = 0; i < listOfProviders.length; i++){
       p = p.then(v => listOfProviders[i]());
    }

   return p;

};

Je crois que ce qui précède est fondamentalement équivalent à:

const executePromises = async function(listOfProviders) {

    for(let i = 0; i < listOfProviders.length; i++){
       await listOfProviders[i]();
    }

};
0
Alexander Mills

J'ai juste eu un problème avec mon fournisseur d'API que faire Promise.all () se retrouverait dans des problèmes de db d'accès simultané.

Le problème avec ma situation est que je dois obtenir chaque résultat de promesse afin de pouvoir afficher une alerte "tout va bien" ou "une erreur obtenue".

Et je ne sais pas pourquoi… ce petit morceau de code qui utilise réduire lorsque les promesses ont été résolues, je ne pouvais pas faire fonctionner mon oscilloscope (trop tard pour enquêter maintenant)

$scope.processArray = function(array) {
    var results = [];
    return array.reduce(function(p, i) {
        return p.then(function() {
            return i.then(function(data) {
                results.Push(data);
                return results;
            })
        });
    }, Promise.resolve());
}

Donc, grâce à cet article http://hellote.com/dynamic-promise-chains/ Je suis venu avec ce petit bâtard .. Ce n'est pas poli, mais ça fonctionne bien.

$scope.recurse = function(promises, promisesLength, results) {

    if (promisesLength === 1) {
        return promises[0].then(function(data){
            results.Push(data);
            return results;
        });
    }

    return promises[promisesLength-1].then(function(data) {
        results.Push(data);
        return $scope.recurse(promises, promisesLength - 1, results);
    });

}

Ensuite, j'appelle la fonction comme ceci:

var recurseFunction = $scope.recurse(promises, promises.length, results);
recurseFunction.then(function (response) { ... });

J'espère que ça aide.

0
Daniel S.

Cette solution basée sur les promesses d'utilisation de EcmaScript 6 a été introduite dans EcmaScript 6 ( https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise ), donc, avant de l'utiliser, consultez le navigateur de table. s support https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise#Browser_compatibility

Code

var f1 = function(){
    for (var i = 0; i < 800000000; i++) {}
    console.log('Function1 is done');
}
var f2 = function(){
    for (var i = 0; i < 800000000; i++) {}
    console.log('Function2 is done');
}
var f3 = function(){
    for (var i = 0; i < 800000000; i++) {}
    console.log('Function3 is done');
}
var f4 = function(){
    for (var i = 0; i < 800000000; i++) {}
    console.log('Function4 is done');
}


callbacks = function(){

    // copy passed arguments
    var callbacks = arguments;

    // create array functions
    var callbacks = Object.keys(callbacks).map(function(el){ return callbacks[el] });

    var now = Date.now();

    callbacks.reduce(function(previousPromise, currentFunc){
        return previousPromise.then(
            function(){
                currentFunc();
                var seconds = (Date.now() - now) / 1000;
                console.log('Gone', seconds, 'seconds');
            }
        )
    }, Promise.resolve());
}

callbacks(f1, f2, f3, f4);

Résultat dans la console Chrome (les valeurs secondes seront différentes):

Function1 is done
Gone 1.147 seconds
Function2 is done
Gone 2.249 seconds
Function3 is done
Gone 3.35 seconds
Function4 is done
Gone 4.47 seconds

Remarques:

  1. Cela ne fonctionne pas si une fonction contient un timer (pour ce problème J'essaie aussi de jQuery $ Callbacks, $ .Ajax et $ .When, mais ça n'aide pas . La seule décision, ce que j'ai trouvé, utilisation resol () dans le rappel d'une minuterie , mais ce n'est pas acceptable si vous avez terminé les fonctions.).
  2. Environnement de test

$ google-chrome --version
Google Chrome 53.0.2785.116
0
Seti Volkylany