Was wäre der idiomatische Weg, um so etwas wie eine while-Schleife mit Versprechungen zu machen? Damit:
etwas tun, wenn die Bedingung noch besteht. Wiederholen Sie den Vorgang und wiederholen Sie dann etwas anderes.
dosomething.then(possilblydomoresomethings).then(finish)
Ich habe es so gemacht, dass ich mich gefragt habe, ob es bessere / idomatischere Wege gibt.
var q = require('q');
var index = 1;
var useless = function(){
var currentIndex = index;
console.log(currentIndex)
var deferred = q.defer();
setTimeout(function(){
if(currentIndex > 10)
deferred.resolve(false);
else deferred.resolve(true);
},500);
return deferred.promise;
}
var control = function(cont){
var deferred = q.defer();
if(cont){
index = index + 1;
useless().then(control).then(function(){
deferred.resolve();
});
}
else deferred.resolve();
return deferred.promise;
}
var chain = useless().then(control).then(function(){console.log('done')});
Ausgabe: 1 2 3 4 5 6 7 8 9 10 11 fertig
Antworten:
Ich würde ein Objekt verwenden, um den Wert zu verpacken. Auf diese Weise können Sie eine
done
Eigenschaft haben, die die Schleife darüber informiert, dass Sie fertig sind.// fn should return an object like // { // done: false, // value: foo // } function loop(promise, fn) { return promise.then(fn).then(function (wrapper) { return !wrapper.done ? loop(Q(wrapper.value), fn) : wrapper.value; }); } loop(Q.resolve(1), function (i) { console.log(i); return { done: i > 10, value: i++ }; }).done(function () { console.log('done'); });
quelle
i++
durch++i
sonst werden Sie eine „Endlosschleife“ bekommen.require ('promise');
Bibliothek. Ich frage mich jetzt, ob eine nicht rekursive Lösung erstellt werden könnte, siehe stackoverflow.com/questions/36361827/…Hier ist eine wiederverwendbare Funktion, die meiner Meinung nach ziemlich klar ist.
var Q = require("q"); // `condition` is a function that returns a boolean // `body` is a function that returns a promise // returns a promise for the completion of the loop function promiseWhile(condition, body) { var done = Q.defer(); function loop() { // When the result of calling `condition` is no longer true, we are // done. if (!condition()) return done.resolve(); // Use `when`, in case `body` does not return a promise. // When it completes loop again otherwise, if it fails, reject the // done promise Q.when(body(), loop, done.reject); } // Start running the loop in the next tick so that this function is // completely async. It would be unexpected if `body` was called // synchronously the first time. Q.nextTick(loop); // The promise return done.promise; } // Usage var index = 1; promiseWhile(function () { return index <= 11; }, function () { console.log(index); index++; return Q.delay(500); // arbitrary async }).then(function () { console.log("done"); }).done();
quelle
Dies ist der einfachste Weg, den ich gefunden habe, um das Grundmuster auszudrücken: Sie definieren eine Funktion, die das Versprechen aufruft, das Ergebnis überprüft und sich dann entweder erneut selbst aufruft oder beendet.
const doSomething = value => new Promise(resolve => setTimeout(() => resolve(value >= 5 ? 'ok': 'no'), 1000)) const loop = value => doSomething(value).then(result => { console.log(value) if (result === 'ok') { console.log('yay') } else { return loop(value + 1) } }) loop(1).then(() => console.log('all done!'))
Sehen Sie es in Aktion auf JSBin
Wenn Sie ein Versprechen verwenden, das aufgelöst oder abgelehnt wird, würden Sie eine if-Klausel definieren
then
undcatch
nicht verwenden.Wenn Sie eine Reihe von Versprechungen hätten, würden Sie einfach
loop
jedes Mal wechseln , um die nächste zu verschieben oder zu knallen.EDIT: Hier ist eine Version, die verwendet
async/await
, weil es 2018 ist:const loop = async value => { let result = null while (result != 'ok') { console.log(value) result = await doSomething(value) value = value + 1 } console.log('yay') }
Sehen Sie es in Aktion auf CodePen
Wie Sie sehen können, wird eine normale while-Schleife und keine Rekursion verwendet.
quelle
Dies ist für Bluebird nicht q, aber da Sie q nicht speziell erwähnt haben, erwähnt der Autor im Bluebird-API-Dokument, dass die Rückgabe einer vielversprechenden Funktion idiomatischer wäre als die Verwendung von verzögerten Funktionen.
var Promise = require('bluebird'); var i = 0; var counter = Promise.method(function(){ return i++; }) function getAll(max, results){ var results = results || []; return counter().then(function(result){ results.push(result); return (result < max) ? getAll(max, results) : results }) } getAll(10).then(function(data){ console.log(data); })
quelle
SCAN
), dass die Rekursion zu viel Stapel erzeugt und entweder für große Datenmengen fehlschlägt oder unnötigen Speicher verbraucht. Ich denke, es6-Generatoren könnten der Weg sein, den ich gehen muss.Da ich die Antwort von Stuart K nicht kommentieren kann, werde ich hier ein wenig hinzufügen. Basierend auf der Antwort von Stuart K können Sie es auf ein überraschend einfaches Konzept reduzieren : Verwenden Sie ein unerfülltes Versprechen wieder . Was er hat, ist im Wesentlichen:
Stuarts Antwort ist für eine allgemeinere Lösung, aber die Grundlagen sind fantastisch (sobald Sie erkennen, wie es funktioniert).
quelle
Dieses Muster kann jetzt einfacher mit q-flow aufgerufen werden . Ein Beispiel für das obige Problem:
var q = require('q'); require('q-flow'); var index = 1; q.until(function() { return q.delay(500).then(function() { console.log(index++); return index > 10; }); }).done(function() { return console.log('done'); });
quelle
Hier ist eine Erweiterung des
Promise
Prototyps, um das Verhalten einerfor
Schleife nachzuahmen . Es unterstützt Versprechen oder Sofortwerte für die Abschnitte Initialisierung, Bedingung, Schleifenkörper und Inkrement. Es bietet auch volle Unterstützung für Ausnahmen und keine Speicherlecks. Im Folgenden finden Sie ein Beispiel für die Verwendung.var Promise = require('promise'); // Promise.loop([properties: object]): Promise() // // Execute a loop based on promises. Object 'properties' is an optional // argument with the following fields: // // initialization: function(): Promise() | any, optional // // Function executed as part of the initialization of the loop. If // it returns a promise, the loop will not begin to execute until // it is resolved. // // Any exception occurring in this function will finish the loop // with a rejected promise. Similarly, if this function returns a // promise, and this promise is reject, the loop finishes right // away with a rejected promise. // // condition: function(): Promise(result: bool) | bool, optional // // Condition evaluated in the beginning of each iteration of the // loop. The function should return a boolean value, or a promise // object that resolves with a boolean data value. // // Any exception occurring during the evaluation of the condition // will finish the loop with a rejected promise. Similarly, it this // function returns a promise, and this promise is rejected, the // loop finishes right away with a rejected promise. // // If no condition function is provided, an infinite loop is // executed. // // body: function(): Promise() | any, optional // // Function acting as the body of the loop. If it returns a // promise, the loop will not proceed until this promise is // resolved. // // Any exception occurring in this function will finish the loop // with a rejected promise. Similarly, if this function returns a // promise, and this promise is reject, the loop finishes right // away with a rejected promise. // // increment: function(): Promise() | any, optional // // Function executed at the end of each iteration of the loop. If // it returns a promise, the condition of the loop will not be // evaluated again until this promise is resolved. // // Any exception occurring in this function will finish the loop // with a rejected promise. Similarly, if this function returns a // promise, and this promise is reject, the loop finishes right // away with a rejected promise. // Promise.loop = function(properties) { // Default values properties = properties || {}; properties.initialization = properties.initialization || function() { }; properties.condition = properties.condition || function() { return true; }; properties.body = properties.body || function() { }; properties.increment = properties.increment || function() { }; // Start return new Promise(function(resolve, reject) { var runInitialization = function() { Promise.resolve().then(function() { return properties.initialization(); }) .then(function() { process.nextTick(runCondition); }) .catch(function(error) { reject(error); }); } var runCondition = function() { Promise.resolve().then(function() { return properties.condition(); }) .then(function(result) { if (result) process.nextTick(runBody); else resolve(); }) .catch(function(error) { reject(error); }); } var runBody = function() { Promise.resolve().then(function() { return properties.body(); }) .then(function() { process.nextTick(runIncrement); }) .catch(function(error) { reject(error); }); } var runIncrement = function() { Promise.resolve().then(function() { return properties.increment(); }) .then(function() { process.nextTick(runCondition); }) .catch(function(error) { reject(error); }); } // Start running initialization process.nextTick(runInitialization); }); } // Promise.delay(time: double): Promise() // // Returns a promise that resolves after the given delay in seconds. // Promise.delay = function(time) { return new Promise(function(resolve) { setTimeout(resolve, time * 1000); }); } // Example var i; Promise.loop({ initialization: function() { i = 2; }, condition: function() { return i < 6; }, body: function() { // Print "i" console.log(i); // Exception when 5 is reached if (i == 5) throw Error('Value of "i" reached 5'); // Wait 1 second return Promise.delay(1); }, increment: function() { i++; } }) .then(function() { console.log('LOOP FINISHED'); }) .catch(function(error) { console.log('EXPECTED ERROR:', error.message); });
quelle
var Q = require('q') var vetor = ['a','b','c'] function imprimeValor(elements,initValue,defer){ console.log( elements[initValue++] ) defer.resolve(initValue) return defer.promise } function Qloop(initValue, elements,defer){ Q.when( imprimeValor(elements, initValue, Q.defer()), function(initValue){ if(initValue===elements.length){ defer.resolve() }else{ defer.resolve( Qloop(initValue,elements, Q.defer()) ) } }, function(err){ defer.reject(err) }) return defer.promise } Qloop(0, vetor,Q.defer())
quelle
Ich benutze jetzt Folgendes:
function each(arr, work) { function loop(arr, i) { return new Promise(function(resolve, reject) { if (i >= arr.length) {resolve();} else try { Promise.resolve(work(arr[i], i)).then(function() { resolve(loop(arr, i+1)) }).catch(reject); } catch(e) {reject(e);} }); } return loop(arr, 0); }
Dies akzeptiert ein Array
arr
und eine Funktionwork
und gibt a zurückPromise
. Die angegebene Funktion wird einmal für jedes Element im Array aufgerufen und übergibt das aktuelle Element und seinen Index im Array. Es kann synchron oder asynchron sein. In diesem Fall muss ein Versprechen zurückgegeben werden.Sie können es so verwenden:
var items = ['Hello', 'cool', 'world']; each(items, function(item, idx) { // this could simply be sync, but can also be async // in which case it must return a Promise return new Promise(function(resolve){ // use setTimeout to make this async setTimeout(function(){ console.info(item, idx); resolve(); }, 1000); }); }) .then(function(){ console.info('DONE'); }) .catch(function(error){ console.error('Failed', error); })
Jedes Element im Array wird nacheinander behandelt. Sobald alle behandelt sind, wird der Code für
.then()
ausgeführt, oder, falls ein Fehler aufgetreten ist, der Code für.catch()
. Innerhalb derwork
Funktion können Siethrow
einError
(im Fall von synchronen Funktionen) oderreject
denPromise
(im Fall von Asynchron - Funktionen) die Schleife abzubrechen.Code-Snippet anzeigen
function each(arr, work) { function loop(arr, i) { return new Promise(function(resolve, reject) { if (i >= arr.length) {resolve();} else try { Promise.resolve(work(arr[i], i)).then(function() { resolve(loop(arr, i+1)) }).catch(reject); } catch(e) {reject(e);} }); } return loop(arr, 0); } var items = ['Hello', 'cool', 'world']; each(items, function(item, idx) { // this could simply be sync, but can also be async // in which case it must return a Promise return new Promise(function(resolve){ // use setTimeout to make this async setTimeout(function(){ console.info(item, idx); resolve(); }, 1000); }); }) .then(function(){ console.info('DONE'); }) .catch(function(error){ console.error('Failed', error); })
quelle
Mit dem ES6-Versprechen habe ich mir das ausgedacht. Es verkettet die Versprechen und gibt ein Versprechen zurück. Es ist technisch gesehen keine while-Schleife, zeigt aber, wie Versprechen synchron durchlaufen werden.
function chain_promises(list, fun) { return list.reduce( function (promise, element) { return promise.then(function () { // I only needed to kick off some side-effects. If you need to get // a list back, you would append to it here. Or maybe use // Array.map instead of Array.reduce. fun(element); }); }, // An initial promise just starts things off. Promise.resolve(true) ); } // To test it... function test_function (element) { return new Promise(function (pass, _fail) { console.log('Processing ' + element); pass(true); }); } chain_promises([1, 2, 3, 4, 5], test_function).then(function () { console.log('Done.'); });
Hier ist meine Geige.
quelle
Promise.resolve(true)
anstelle desnew Promise
KonstruktorsIch dachte, ich könnte genauso gut meinen Hut mit ES6 Promises in den Ring werfen ...
function until_success(executor){ var before_retry = undefined; var outer_executor = function(succeed, reject){ var rejection_handler = function(err){ if(before_retry){ try { var pre_retry_result = before_retry(err); if(pre_retry_result) return succeed(pre_retry_result); } catch (pre_retry_error){ return reject(pre_retry_error); } } return new Promise(executor).then(succeed, rejection_handler); } return new Promise(executor).then(succeed, rejection_handler); } var outer_promise = new Promise(outer_executor); outer_promise.before_retry = function(func){ before_retry = func; return outer_promise; } return outer_promise; }
Das
executor
Argument ist das gleiche wie das an einenPromise
Konstruktor übergebene, wird jedoch wiederholt aufgerufen, bis der erfolgreiche Rückruf ausgelöst wird. Diebefore_retry
Funktion ermöglicht die benutzerdefinierte Fehlerbehandlung bei fehlgeschlagenen Versuchen. Wenn es einen wahrheitsgemäßen Wert zurückgibt, wird es als eine Form des Erfolgs angesehen und die "Schleife" endet, mit dieser Wahrheit als Ergebnis. Wenn keinebefore_retry
Funktion registriert ist oder einen Falsey-Wert zurückgibt, wird die Schleife für eine weitere Iteration ausgeführt. Die dritte Option ist, dass diebefore_retry
Funktion selbst einen Fehler auslöst. In diesem Fall endet die "Schleife" und übergibt diesen Fehler als Fehler.Hier ist ein Beispiel:
var counter = 0; function task(succ, reject){ setTimeout(function(){ if(++counter < 5) reject(counter + " is too small!!"); else succ(counter + " is just right"); }, 500); // simulated async task } until_success(task) .before_retry(function(err){ console.log("failed attempt: " + err); // Option 0: return falsey value and move on to next attempt // return // Option 1: uncomment to get early success.. //if(err === "3 is too small!!") // return "3 is sort of ok"; // Option 2: uncomment to get complete failure.. //if(err === "3 is too small!!") // throw "3rd time, very unlucky"; }).then(function(val){ console.log("finally, success: " + val); }).catch(function(err){ console.log("it didn't end well: " + err); })
Ausgabe für Option 0:
failed attempt: 1 is too small!! failed attempt: 2 is too small!! failed attempt: 3 is too small!! failed attempt: 4 is too small!! finally, success: 5 is just right
Ausgabe für Option 1:
failed attempt: 1 is too small!! failed attempt: 2 is too small!! failed attempt: 3 is too small!! finally, success: 3 is sort of ok
Ausgabe für Option 2:
failed attempt: 1 is too small!! failed attempt: 2 is too small!! failed attempt: 3 is too small!! it didn't end well: 3rd time, very unlucky
quelle
Viele Antworten hier und was Sie erreichen wollen, ist nicht sehr praktisch. aber das sollte funktionieren. Dies wurde in einer aws-Lambda-Funktion implementiert, mit Node.js 10 dauert es bis zum Timeout der Funktion. Es kann auch eine anständige Menge an Speicher verbrauchen.
exports.handler = async (event) => { let res = null; while (true) { try{ res = await dopromise(); }catch(err){ res = err; } console.log(res); }//infinite will time out }; function dopromise(){ return new Promise((resolve, reject) => { //do some logic //if error reject //reject('failed'); resolve('success'); }); }
Auf Lambda getestet und über 5 Minuten gut laufen. Aber wie von anderen gesagt, ist dies keine gute Sache.
quelle
Ich habe ein Modul geschrieben, mit dem Sie verkettete Schleifen asynchroner Aufgaben mit Versprechungen ausführen können. Es basiert auf der obigen Antwort von juandopazo
/** * Should loop over a task function which returns a "wrapper" object * until wrapper.done is true. A seed value wrapper.seed is propagated to the * next run of the loop. * * todo/maybe? Reject if wrapper is not an object with done and seed keys. * * @param {Promise|*} seed * @param {Function} taskFn * * @returns {Promise.<*>} */ function seedLoop(seed, taskFn) { const seedPromise = Promise.resolve(seed); return seedPromise .then(taskFn) .then((wrapper) => { if (wrapper.done) { return wrapper.seed; } return seedLoop(wrapper.seed, taskFn); }); } // A super simple example of counting to ten, which doesn't even // do anything asynchronous, but if it did, it should resolve to // a promise that returns the { done, seed } wrapper object for the // next call of the countToTen task function. function countToTen(count) { const done = count > 10; const seed = done ? count : count + 1; return {done, seed}; } seedLoop(1, countToTen).then((result) => { console.log(result); // 11, the first value which was over 10. });
https://github.com/CascadeEnergy/promise-seedloop
quelle