Comment construire correctement une boucle pour s'assurer que l'appel suivant promise et le chaîné logger.log (res) fonctionnent de manière synchrone lors de l'itération (oiseau bleu)
db.getUser(email).then(function(res) { logger.log(res); }); // this is a promise
J'ai essayé le moyen suivant (méthode de http://blog.victorquinn.com/javascript-promise-while-loop )
var Promise = require('bluebird');
var promiseWhile = function(condition, action) {
var resolver = Promise.defer();
var loop = function() {
if (!condition()) return resolver.resolve();
return Promise.cast(action())
.then(loop)
.catch(resolver.reject);
};
process.nextTick(loop);
return resolver.promise;
});
var count = 0;
promiseWhile(function() {
return count < 10;
}, function() {
return new Promise(function(resolve, reject) {
db.getUser(email)
.then(function(res) {
logger.log(res);
count++;
resolve();
});
});
}).then(function() {
console.log('all done');
});
Bien que cela semble fonctionner, mais je ne pense pas que cela garantisse l'ordre d'appeler logger.log (res);
Aucune suggestion?
Je ne pense pas que cela garantisse l'ordre d'appeler logger.log (res);
En fait, c'est le cas. Cette instruction est exécutée avant l'appel resolve
.
Aucune suggestion?
Beaucoup. Le plus important est votre utilisation de créez-une-promesse-anti-modèle manuellement - faites seulement
promiseWhile(…, function() {
return db.getUser(email)
.then(function(res) {
logger.log(res);
count++;
});
})…
Deuxièmement, cette fonction while
pourrait être beaucoup simplifiée:
var promiseWhile = Promise.method(function(condition, action) {
if (!condition()) return;
return action().then(promiseWhile.bind(null, condition, action));
});
Troisièmement, je n’utiliserais pas une boucle while
(avec une variable de fermeture) mais une boucle for
:
var promiseFor = Promise.method(function(condition, action, value) {
if (!condition(value)) return value;
return action(value).then(promiseFor.bind(null, condition, action));
});
promiseFor(function(count) {
return count < 10;
}, function(count) {
return db.getUser(email)
.then(function(res) {
logger.log(res);
return ++count;
});
}, 0).then(console.log.bind(console, 'all done'));
Si vous voulez vraiment une fonction promiseWhen()
générale pour cela et d'autres objectifs, alors n'hésitez pas, faites-le, en utilisant les simplifications de Bergi. Cependant, en raison de la manière dont les promesses fonctionnent, passer des rappels de cette manière est généralement inutile et vous oblige à franchir des étapes complexes.
Autant que je sache, vous essayez:
.then()
via récursivité.Ainsi défini, le problème est en réalité celui qui a été discuté dans "The Collection Kerfuffle" dans Promise Anti-patterns , qui offre deux solutions simples:
Array.prototype.map()
Array.prototype.reduce()
.L’approche parallèle donnera (directement) le problème que vous essayez d’éviter, à savoir que l’ordre des réponses est incertain. L’approche en série construira la chaîne .then()
requise - à plat - sans récursivité.
function fetchUserDetails(arr) {
return arr.reduce(function(promise, email) {
return promise.then(function() {
return db.getUser(email).done(function(res) {
logger.log(res);
});
});
}, Promise.resolve());
}
Appeler comme suit:
//Compose here, by whatever means, an array of email addresses.
var arrayOfEmailAddys = [...];
fetchUserDetails(arrayOfEmailAddys).then(function() {
console.log('all done');
});
Comme vous pouvez le constater, il n’est pas nécessaire d’utiliser la très variable var count
ou sa fonction condition
associée. La limite (de 10 dans la question) est entièrement déterminée par la longueur du tableau arrayOfEmailAddys
.
Voici comment je le fais avec l'objet Promise standard.
// Given async function sayHi
function sayHi() {
return new Promise((resolve) => {
setTimeout(() => {
console.log('Hi');
resolve();
}, 3000);
});
}
// And an array of async functions to loop through
const asyncArray = [sayHi, sayHi, sayHi];
// We create the start of a promise chain
let chain = Promise.resolve();
// And append each function in the array to the promise chain
for (const func of asyncArray) {
chain = chain.then(func);
}
// Output:
// Hi
// Hi (After 3 seconds)
// Hi (After 3 more seconds)
Donné
Champs obligatoires
Solution
let asyncFn = (item) => {
return new Promise((resolve, reject) => {
setTimeout( () => {console.log(item); resolve(true)}, 1000 )
})
}
// asyncFn('a')
// .then(()=>{return async('b')})
// .then(()=>{return async('c')})
// .then(()=>{return async('d')})
let a = ['a','b','c','d']
a.reduce((previous, current, index, array) => {
return previous // initiates the promise chain
.then(()=>{return asyncFn(array[index])}) //adds .then() promise for each item
}, Promise.resolve())
Je ferais quelque chose comme ça:
var request = []
while(count<10){
request.Push(db.getUser(email).then(function(res) { return res; }));
count++
};
Promise.all(request).then((dataAll)=>{
for (var i = 0; i < dataAll.length; i++) {
logger.log(dataAll[i]);
}
});
de cette manière, dataAll est un tableau ordonné de tous les éléments à consigner. Et l’opération de journalisation s’effectuera lorsque toutes les promesses seront faites.
Il existe un nouveau moyen de résoudre ce problème en utilisant async/wait.
async function myFunction() {
while(/* my condition */) {
const res = await db.getUser(email);
logger.log(res);
}
}
myFunction().then(() => {
/* do other stuff */
})
https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Statements/async_functionhttps://ponyfoo.com/articles/understanding-javascript-async attendre
La fonction suggérée par Bergi est vraiment agréable:
var promiseWhile = Promise.method(function(condition, action) {
if (!condition()) return;
return action().then(promiseWhile.bind(null, condition, action));
});
Je veux tout de même faire un petit ajout, ce qui est logique lorsque j'utilise des promesses:
var promiseWhile = Promise.method(function(condition, action, lastValue) {
if (!condition()) return lastValue;
return action().then(promiseWhile.bind(null, condition, action));
});
De cette façon, la boucle while peut être intégrée à une chaîne de promesse et résolue avec lastValue (même si l'action () n'est jamais exécutée). Voir exemple:
var count = 10;
util.promiseWhile(
function condition() {
return count > 0;
},
function action() {
return new Promise(function(resolve, reject) {
count = count - 1;
resolve(count)
})
},
count)
Utiliser l'objet de promesse standard et demander à la promesse de renvoyer les résultats.
function promiseMap (data, f) {
const reducer = (promise, x) =>
promise.then(acc => f(x).then(y => acc.Push(y) && acc))
return data.reduce(reducer, Promise.resolve([]))
}
var emails = []
function getUser(email) {
return db.getUser(email)
}
promiseMap(emails, getUser).then(emails => {
console.log(emails)
})
Prenez d’abord un tableau de promesses (tableau de promesses) puis résolvez-les à l’aide de Promise.all(promisearray)
.
var arry=['raju','ram','abdul','kruthika'];
var promiseArry=[];
for(var i=0;i<arry.length;i++) {
promiseArry.Push(dbFechFun(arry[i]));
}
Promise.all(promiseArry)
.then((result) => {
console.log(result);
})
.catch((error) => {
console.log(error);
});
function dbFetchFun(name) {
// we need to return a promise
return db.find({name:name}); // any db operation we can write hear
}
Utilisez async et wait (es6):
function taskAsync(paramets){
return new Promise((reslove,reject)=>{
//your logic after reslove(respoce) or reject(error)
})
}
async function fName(){
let arry=['list of items'];
for(var i=0;i<arry.length;i++){
let result=await(taskAsync('parameters'));
}
}
function promiseLoop(promiseFunc, paramsGetter, conditionChecker, eachFunc, delay) {
function callNext() {
return promiseFunc.apply(null, paramsGetter())
.then(eachFunc)
}
function loop(promise, fn) {
if (delay) {
return new Promise(function(resolve) {
setTimeout(function() {
resolve();
}, delay);
})
.then(function() {
return promise
.then(fn)
.then(function(condition) {
if (!condition) {
return true;
}
return loop(callNext(), fn)
})
});
}
return promise
.then(fn)
.then(function(condition) {
if (!condition) {
return true;
}
return loop(callNext(), fn)
})
}
return loop(callNext(), conditionChecker);
}
function makeRequest(param) {
return new Promise(function(resolve, reject) {
var req = https.request(function(res) {
var data = '';
res.on('data', function (chunk) {
data += chunk;
});
res.on('end', function () {
resolve(data);
});
});
req.on('error', function(e) {
reject(e);
});
req.write(param);
req.end();
})
}
function getSomething() {
var param = 0;
var limit = 10;
var results = [];
function paramGetter() {
return [param];
}
function conditionChecker() {
return param <= limit;
}
function callback(result) {
results.Push(result);
param++;
}
return promiseLoop(makeRequest, paramGetter, conditionChecker, callback)
.then(function() {
return results;
});
}
getSomething().then(function(res) {
console.log('results', res);
}).catch(function(err) {
console.log('some error along the way', err);
});
Que diriez-vous de celui-ci en utilisant BlueBird ?
function fetchUserDetails(arr) {
return Promise.each(arr, function(email) {
return db.getUser(email).done(function(res) {
logger.log(res);
});
});
}
Voici une autre méthode (ES6 w/std Promise). Utilise les critères de sortie de type lodash/underscore (return === false). Notez que vous pouvez facilement ajouter une méthode exitIf () dans les options à exécuter dans doOne ().
const whilePromise = (fnReturningPromise,options = {}) => {
// loop until fnReturningPromise() === false
// options.delay - setTimeout ms (set to 0 for 1 tick to make non-blocking)
return new Promise((resolve,reject) => {
const doOne = () => {
fnReturningPromise()
.then((...args) => {
if (args.length && args[0] === false) {
resolve(...args);
} else {
iterate();
}
})
};
const iterate = () => {
if (options.delay !== undefined) {
setTimeout(doOne,options.delay);
} else {
doOne();
}
}
Promise.resolve()
.then(iterate)
.catch(reject)
})
};