Wie kann eine Schleife korrekt erstellt werden, um sicherzustellen, dass der folgende promise-Aufruf und der verkettete logger.log (res) synchron durch die Iteration laufen? (bluebird)
db.getUser(email).then(function(res) { logger.log(res); }); // this is a promise
Ich habe den folgenden Weg ausprobiert (Methode aus 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');
});
Es scheint zwar zu funktionieren, aber ich glaube nicht, dass es die Reihenfolge des Aufrufs von logger.log (res) garantiert.
Irgendwelche Vorschläge?
Ich glaube nicht, dass es die Reihenfolge des Aufrufs von logger.log (res) garantiert.
Eigentlich tut es. Diese Anweisung wird vor dem Aufruf von resolve
ausgeführt.
Irgendwelche Vorschläge?
Viele. Das wichtigste ist, dass Sie das create-versprechen-manuelles Gegenmuster verwenden - tun Sie es nur
promiseWhile(…, function() {
return db.getUser(email)
.then(function(res) {
logger.log(res);
count++;
});
})…
Zweitens könnte diese while
-Funktion stark vereinfacht werden:
var promiseWhile = Promise.method(function(condition, action) {
if (!condition()) return;
return action().then(promiseWhile.bind(null, condition, action));
});
Drittens würde ich keine while
-Schleife (mit einer Abschlussvariablen) verwenden, sondern eine for
-Schleife:
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'));
Wenn Sie wirklich eine allgemeine promiseWhen()
-Funktion für diese und andere Zwecke haben möchten, verwenden Sie Bergis Vereinfachungen. Aufgrund der Art und Weise, wie Versprechen funktionieren, ist das Weiterleiten von Rückrufen auf diese Weise im Allgemeinen unnötig und zwingt Sie, durch komplexe kleine Reifen zu springen.
Soweit ich weiß, dass Sie es versuchen:
.then()
-Kette aufbauen.So definiert ist das Problem eigentlich das unter "The Collection Kerfuffle" in Promise Anti-pattern beschriebene, das zwei einfache Lösungen bietet:
Array.prototype.map()
Array.prototype.reduce()
.Der parallele Ansatz gibt (unkompliziert) das Problem an, das Sie zu vermeiden versuchen - die Reihenfolge der Antworten ist unsicher. Der serielle Ansatz erstellt die erforderliche .then()
-Kette - flach - keine Rekursion.
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());
}
Rufen Sie wie folgt an:
//Compose here, by whatever means, an array of email addresses.
var arrayOfEmailAddys = [...];
fetchUserDetails(arrayOfEmailAddys).then(function() {
console.log('all done');
});
Wie Sie sehen, besteht keine Notwendigkeit für die hässliche äußere var count
oder die zugehörige condition
-Funktion. Die Grenze (von 10 in der Frage) wird ausschließlich durch die Länge des Arrays arrayOfEmailAddys
bestimmt.
So mache ich es mit dem Standard-Promise-Objekt.
// 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)
Gegeben
Erforderlich
Lösung
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())
Ich würde so etwas machen:
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]);
}
});
auf diese Weise ist dataAll ein geordnetes Array aller zu protokollierenden Elemente. Die Protokolloperation wird ausgeführt, wenn alle Versprechen erfüllt sind.
Es gibt einen neuen Weg, dies zu lösen, und zwar mit async/await.
async function myFunction() {
while(/* my condition */) {
const res = await db.getUser(email);
logger.log(res);
}
}
myFunction().then(() => {
/* do other stuff */
})
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_functionhttps://ponyfoo.com/articles/understanding-javascript-async- warten
Die vorgeschlagene Funktion von Bergi ist wirklich Nizza:
var promiseWhile = Promise.method(function(condition, action) {
if (!condition()) return;
return action().then(promiseWhile.bind(null, condition, action));
});
Trotzdem möchte ich eine kleine Ergänzung machen, was bei Versprechungen sinnvoll ist:
var promiseWhile = Promise.method(function(condition, action, lastValue) {
if (!condition()) return lastValue;
return action().then(promiseWhile.bind(null, condition, action));
});
Auf diese Weise kann die While-Schleife in eine Versprechungskette eingebettet werden und wird mit lastValue aufgelöst (auch wenn die Aktion () niemals ausgeführt wird). Siehe Beispiel:
var count = 10;
util.promiseWhile(
function condition() {
return count > 0;
},
function action() {
return new Promise(function(resolve, reject) {
count = count - 1;
resolve(count)
})
},
count)
Verwenden des Standardversprechungsobjekts und Rückgabe der Ergebnisse durch das Versprechen.
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)
})
Nehmen Sie zuerst das Array der Versprechungen (Promise Array) und lösen Sie anschließend dieses Versprechen-Array mit Promise.all(promisearray)
auf.
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
}
Verwenden Sie async und warten Sie ab (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);
});
Wie wäre es mit BlueBird ?
function fetchUserDetails(arr) {
return Promise.each(arr, function(email) {
return db.getUser(email).done(function(res) {
logger.log(res);
});
});
}
Hier ist eine andere Methode (ES6 mit std Promise). Verwendet Lodash-/Unterstrich-Austrittskriterien (return === false). Beachten Sie, dass Sie leicht eine exitIf () - Methode in Optionen hinzufügen können, um sie in doOne () auszuführen.
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)
})
};