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)
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>
É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');
});
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);
});
}
}
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
.
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]();
}
};
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.
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:
$ google-chrome --version
Google Chrome 53.0.2785.116