Recursion in async/await not resolve in main function - node.js

I am trying to work with recursion in asynс/await functions. The problem is that I cannot get the final promise in the main function, in which I initially call the recursive method
async function delay(ms) {
return await new Promise(resolve => setTimeout(resolve, ms))
}
async function recursion(i) {
return new Promise(async (resolve, reject) => {
if (i == 0) {
console.log(`i == 0`)
resolve(i)
} else {
console.log(`i = ${i}. Wait 1 second...`)
i--
await delay(2000)
await recursion(i)
}
})
}
async function main() {
let i = await recursion(3)
console.log(`END OF RECURSION`) //This code never use!
console.log(`i => ${i}`)
}
main()
console.log:
i = 3. Wait 1 second...
i = 2. Wait 1 second...
i = 1. Wait 1 second...
i == 0

This is promise construction antipattern. There's already existing promise to chain, no need to create a new one. A promise that is returned from Promise callback is ignored, this breaks promise chain. Also, there is an inconsistency, the value isn't always returned from recursion.
It should be:
async function recursion(i) {
if (i == 0) {
console.log(`i == 0`)
return i
} else {
console.log(`i = ${i}. Wait 1 second...`)
i--
await delay(2000)
return recursion(i)
}
}

Related

Asynchronous function not waiting for Promise

I've written following asynchronious node.js function, which accesses to my database via Mongoose and thus is an async function):
function getWindSpeed(householdID){
return new Promise(async function (resolve, _){
const household = await Household.findById(householdID)
resolve(stoch.norm(household.windSimulation.mu, household.windSimulation.sigma, 1))
})
}
In the other hand, I have following function, which is also async because both accesses to the database and uses the previous function for each element in the database:
async function getMeanWindSpeed() {
return new Promise(async function (resolve, reject){
let numberOfHouseholds = 0
let averageWind = 0.0
console.log('Begin')
await Household.find({}, async function (error, households) {
if(error){
reject(error)
}else{
numberOfHouseholds = households.length
for(let i = 0; i < households.length; i++){
const speed = await wind.getWindSpeed(households[i].id)
console.log(speed)
averageWind += speed
}
}
})
averageWind = averageWind / numberOfHouseholds
console.log('Finish')
resolve(averageWind)
})
}
As you can see, I iterate over all the elements in the collection and apply the getWindSpeed() function, however it doesn't wait for its completion, as I get the following trace based on the console.log(...) debug messaged:
Begin
Finish
12.2322
23.1123
(more speeds)
...
Some more information that may be usefuk:
I'm awaiting for the result of getMeanWindSpeed() in another async function
I tried to return one hardcoded value for each element in the database (instead of calling getWindSpeed() and it worked fined, so I guess the problem is in that function.
Thanks in advance
If we don't pass a callback to .find() we'll get a promise returned, this makes the code a lot easier to read.
We could further simplify the function getMeanWindspeed since it becomes a simple wrapper for getAverageWindSpeed();
For example:
async function getAverageWindspeed() {
let numberOfHouseholds = 0
let averageWind = 0.0
let households = await Household.find({});
numberOfHouseholds = households.length
for(let i = 0; i < households.length; i++){
const speed = await wind.getWindSpeed(households[i].id)
console.log(speed)
averageWind += speed
}
return averageWind / numberOfHouseholds;
}
async function getMeanWindSpeed() {
console.log('Begin')
let averageWind = await getAverageWindspeed();
console.log('Finish')
return averageWind;
}
Why you are mixing await with promise. It is bad practice. If you can do same thing using await and async. See the below example.
const fakeDelay = () => new Promise(r => {
setTimeout(() => r("data"), 1000);
})
const Household = {
findById: () => fakeDelay()
}
async function getWindSpeed(householdID){
const household = await Household.findById(householdID)
console.log()
//stoch.norm(household.windSimulation.mu, household.windSimulation.sigma, 1)
return household;
}
const main = async () =>{
getWindSpeed().then(console.log)
}
main()
// notice
async function getWindSpeed
will be by default promise

Promise Jump Alwasy to The catch

I have an async function (a Promise) which does some things. I have to call it N times.
Every call represents a simulation point.
My first guess was to use a loop:
for(let i=0; i < N; i++) {
myAsyncFunc(data[i])
.then(() => myAsyncFunc(data[i]) )
}
Obviously, this does not work because the loops and before any subsequent call to myAsyncFun.
How can I call step-by-step the async function, waiting for results and proceed to the next step?
I tried whit this:
function myAsyncFunc(data) {
return new Promise( (resolve, reject) => {
anotherAsync.then(resolve).catch(reject);
}
}
function simulate(mode) {
[...Array(10)].reduce((p, _, i) =>
p.then(_ => new Promise(resolve => {
myAsyncFunc(data[i]); // <== this return a Promise
}
))
, Promise.resolve());
}
But the functions myAsyncFunc are not called in sequence.
I solved by using async/await which, apparently, seems to solve the acrobatics with asynchronous function calls details of JS
function myAsyncFunc(data) {
anotherAsync.then( () => return );
}
async function simulate(mode) {
for(let i=0; i < tempModel[0].linear.length; i++)
{
let a = await myAsyncFunc(mode,i);
}
}

async await with setInterval

function first(){
console.log('first')
}
function second(){
console.log('second')
}
let interval = async ()=>{
await setInterval(first,2000)
await setInterval(second,2000)
}
interval();
Imagine that I have this code above.
When I run it, first() and second() will be called at the same time; how do I call second() after first)() returns some data, for example, if first() is done, only then call second()?
Because first() in my code will be working with a big amount of data and if this 2 functions will be calling at the same time, it will be hard for the server.
How do I call second() each time when first() will return some data?
As mentioned above setInterval does not play well with promises if you do not stop it. In case you clear the interval you can use it like:
async function waitUntil(condition) {
return await new Promise(resolve => {
const interval = setInterval(() => {
if (condition) {
resolve('foo');
clearInterval(interval);
};
}, 1000);
});
}
Later you can use it like
const bar = waitUntil(someConditionHere)
You have a few problems:
Promises may only ever resolve once, setInterval() is meant to call the callback multiple times, Promises do not support this case well.
Neither setInterval(), nor the more appropriate setTimeout() return Promises, therefore, awaiting on them is pointless in this context.
You're looking for a function that returns a Promise which resolves after some times (using setTimeout(), probably, not setInterval()).
Luckily, creating such a function is rather trivial:
async function delay(ms) {
// return await for better async stack trace support in case of errors.
return await new Promise(resolve => setTimeout(resolve, ms));
}
With this new delay function, you can implement your desired flow:
function first(){
console.log('first')
}
function second(){
console.log('second')
}
let run = async ()=>{
await delay(2000);
first();
await delay(2000)
second();
}
run();
setInterval doesn't play well with promises because it triggers a callback multiple times, while promise resolves once.
It seems that it's setTimeout that fits the case. It should be promisified in order to be used with async..await:
async () => {
await new Promise(resolve => setTimeout(() => resolve(first()), 2000));
await new Promise(resolve => setTimeout(() => resolve(second()), 2000));
}
await expression causes async to pause until a Promise is settled
so you can directly get the promise's result without await
for me, I want to initiate Http request every 1s
let intervalid
async function testFunction() {
intervalid = setInterval(() => {
// I use axios like: axios.get('/user?ID=12345').then
new Promise(function(resolve, reject){
resolve('something')
}).then(res => {
if (condition) {
// do something
} else {
clearInterval(intervalid)
}
})
}, 1000)
}
// you can use this function like
testFunction()
// or stop the setInterval in any place by
clearInterval(intervalid)
You could use an IFFE. This way you could escape the issue of myInterval not accepting Promise as a return type.
There are cases where you need setInterval, because you want to call some function unknown amount of times with some interval in between.
When I faced this problem this turned out to be the most straight-forward solution for me. I hope it help someone :)
For me the use case was that I wanted to send logs to CloudWatch but try not to face the Throttle exception for sending more than 5 logs per second. So I needed to keep my logs and send them as a batch in an interval of 1 second. The solution I'm posting here is what I ended up using.
async function myAsyncFunc(): Promise<string> {
return new Promise<string>((resolve) => {
resolve("hello world");
});
}
function myInterval(): void {
setInterval(() => {
void (async () => {
await myAsyncFunc();
})();
}, 5_000);
}
// then call like so
myInterval();
Looked through all the answers but still didn't find the correct one that would work exactly how the OP is asked. This is what I used for the same purpose:
async function waitInterval(callback, ms) {
return new Promise(resolve => {
let iteration = 0;
const interval = setInterval(async () => {
if (await callback(iteration, interval)) {
resolve();
clearInterval(interval);
}
iteration++;
}, ms);
});
}
function first(i) {
console.log(`first: ${i}`);
// If the condition below is true the timer finishes
return i === 5;
}
function second(i) {
console.log(`second: ${i}`);
// If the condition below is true the timer finishes
return i === 5;
}
(async () => {
console.log('start');
await waitInterval(first, 1000);
await waitInterval(second, 1000);
console.log('finish');
})()
In my example, I also put interval iteration count and the timer itself, just in case the caller would need to do something with it. However, it's not necessary
In my case, I needed to iterate through a list of images, pausing in between each, and then a longer pause at the end before re-looping through.
I accomplished this by combining several techniques from above, calling my function recursively and awaiting a timeout.
If at any point another trigger changes my animationPaused:boolean, my recursive function will exit.
const loopThroughImages = async() => {
for (let i=0; i<numberOfImages; i++){
if (animationPaused) {
return;
}
this.updateImage(i);
await timeout(700);
}
await timeout(1000);
loopThroughImages();
}
loopThroughImages();
Async/await do not make the promises synchronous.
To my knowledge, it's just a different syntax for return Promise and .then().
Here i rewrote the async function and left both versions, so you can see what it really does and compare.
It's in fact a cascade of Promises.
// by the way no need for async there. the callback does not return a promise, so no need for await.
function waitInterval(callback, ms) {
return new Promise(resolve => {
let iteration = 0;
const interval = setInterval(async () => {
if (callback(iteration, interval)) {
resolve();
clearInterval(interval);
}
iteration++;
}, ms);
});
}
function first(i) {
console.log(`first: ${i}`);
// If the condition below is true the timer finishes
return i === 5;
}
function second(i) {
console.log(`second: ${i}`);
// If the condition below is true the timer finishes
return i === 5;
}
// async function with async/await, this code ...
(async () => {
console.log('start');
await waitInterval(first, 1000);
await waitInterval(second, 1000);
console.log('finish');
})() //... returns a pending Promise and ...
console.log('i do not wait');
// ... is kinda identical to this code.
// still asynchronous but return Promise statements with then cascade.
(() => {
console.log('start again');
return waitInterval(first, 1000).then(() => {
return waitInterval(second, 1000).then(() => {
console.log('finish again');
});
});
})(); // returns a pending Promise...
console.log('i do not wait either');
You can see the two async functions both execute at the same time.
So using promises around intervals here is not very useful, as it's still just intervals, and promises changes nothing, and make things confusing...
As the code is calling callbacks repeatedly into an interval, this is, i think, a cleaner way:
function first(i) {
console.log(`first: ${i}`);
// If the condition below is true the timer finishes
return i === 5;
}
function second(i) {
console.log(`second: ${i}`);
// If the condition below is true the timer finishes
return i === 5;
}
function executeThroughTime(...callbacks){
console.log('start');
let callbackIndex = 0; // to track current callback.
let timerIndex = 0; // index given to callbacks
let interval = setInterval(() =>{
if (callbacks[callbackIndex](timerIndex++)){ // callback return true when it finishes.
timerIndex = 0; // resets for next callback
if (++callbackIndex>=callbacks.length){ // if no next callback finish.
clearInterval(interval);
console.log('finish');
}
}
},1000)
}
executeThroughTime(first,second);
console.log('and i still do not wait ;)');
Also, this solution execute a callback every secondes.
if the callbacks are async requests that takes more than one sec to resolve, and i can't afford for them to overlap, then, instead of doing iterative call with repetitive interval, i would get the request resolution to call the next request (through a timer if i don't want to harass the server).
Here the "recursive" task is called lTask, does pretty much the same as before, except that, as i do not have an interval anymore, i need a new timer each iteration.
// slow internet request simulation. with a Promise, could be a callback.
function simulateAsync1(i) {
console.log(`first pending: ${i}`);
return new Promise((resolve) =>{
setTimeout(() => resolve('got that first big data'), Math.floor(Math.random()*1000)+ 1000);//simulate request that last between 1 and 2 sec.
}).then((result) =>{
console.log(`first solved: ${i} ->`, result);
return i==2;
});
}
// slow internet request simulation. with a Promise, could be a callback.
function simulateAsync2(i) {
console.log(`second pending: ${i}`);
return new Promise((resolve) =>{
setTimeout(() => resolve('got that second big data'), Math.floor(Math.random()*1000) + 1000);//simulate request that last between 1 and 2 sec.
}).then((result) =>{ // promise is resolved
console.log(`second solved: ${i} ->`,result);
return i==4; // return a promise
});
}
function executeThroughTime(...asyncCallbacks){
console.log('start');
let callbackIndex = 0;
let timerIndex = 0;
let lPreviousTime = Date.now();
let lTask = () => { // timeout callback.
asyncCallbacks[callbackIndex](timerIndex++).then((result) => { // the setTimeout for the next task is set when the promise is solved.
console.log('result',result)
if (result) { // current callback is done.
timerIndex = 0;
if (++callbackIndex>=asyncCallbacks.length){//are all callbacks done ?
console.log('finish');
return;// its over
}
}
console.log('time elapsed since previous call',Date.now() - lPreviousTime);
lPreviousTime = Date.now();
//console.log('"wait" 1 sec (but not realy)');
setTimeout(lTask,1000);//redo task after 1 sec.
//console.log('i do not wait');
});
}
lTask();// no need to set a timer for first call.
}
executeThroughTime(simulateAsync1,simulateAsync2);
console.log('i do not wait');
Next step would be to empty a fifo with the interval, and fill it with web request promises...

How to make a promise based function to be executed a few times with setTimeout before giving up in node 6 (i.e. no javascript specs for async/await)

I need to retrieve the actual value from a promise based function in a node 6 environment (Azure Functions), so I used co (https://www.npmjs.com/package/co) via generators (instead of the async/await paradigm) to handle the inner promise.
I need also to retry a few times that co/promise function using setTimeout before giving up definitively.
I am currently not able to make the following code work as expected. I am not sure where is the problem, but I can not "yield from the promise returned by co", so in the end the array that is passed around the recursive levels of the stack contains promises of values (1/0) rather than the actual values.
This is the wrapper for the "promise based function" that is handled with a try/catch to make sure we actually always return either 1 or 0.
const wannabeSyncFunc = () => {
console.log("outside co...");
return co(function *(){
console.log("inside co...");
try {
console.log("yielding...");
// promise that could be rejected hence try/catch
//
// I can not change this returned promise, so I must treat it
// as a promise that could potentially be rejected
let stuff = yield Promise.resolve();
console.log("stuff?", stuff);
console.log("returning 1");
return 1;
} catch (err) {
console.log("returning 0");
return 0;
}
console.log("after try/catch...");
});
}
This is the recursive/settimeout function that is supposed to try a few times before giving up.
const retryIntervalInMillis = 50;
const wannabeRecursiveFunc = (currTimes, attemptsArray) => {
return co(function *(){
console.log("Curr attemptsArray:", attemptsArray);
console.log("Curr attemptsArray[attemptsArray.length - 1]:", attemptsArray[attemptsArray.length - 1]);
console.log("Curr Promise.resolve(attemptsArray[attemptsArray.length - 1]):", Promise.resolve(attemptsArray[attemptsArray.length - 1]));
if (attemptsArray[attemptsArray.length - 1] == Promise.resolve(1)) {
console.log("Found the solution, returning straight away!")
return attemptsArray;
}
if (currTimes <= 0) {
console.log("Expired acquiring recursion");
return attemptsArray;
}
currTimes--;
const currValue = wannabeSyncFunc();
console.log(`First: currTimes: ${currTimes} currValue: ${currValue} curr attemptsArray: ${attemptsArray}`);
attemptsArray.push(currValue);
if (currValue === 1) {
return attemptsArray;
}
console.log(`Then: currTimes: ${currTimes} curr attemptsArray: ${attemptsArray}`);
return yield setTimeout(wannabeRecursiveFunc, currTimes*retryIntervalInMillis, currTimes, attemptsArray);
// return Promise.all(attemptsArray);
});
}
I've tried to invoke this in a few different ways like:
const numberOfAttempts = 3;
let theArray = wannabeRecursiveFunc(numberOfAttempts, []);
console.log(">>>", theArray);
Or assuming wannabeRecursiveFunc to return a promise and .then after the promise trying to print theArray.
I keep seeing inside the array these elements Promise { 1 } when printing it, but I would like to see either 1 or 0, so I hope those checks before the recursion could work as expected. At the moment those check don't work I think because I am comparing Promise { 1 } with 1.
However, I am not sure this is the reason why the whole thing is not working, and I am not even sure how to fix this. I am not sure whether co is needed (even in the node.js v6 environment), and how to make this promise/setTimeout work as expected.
I think I understand your objective: invoke a function that might fail, if it fails, wait a little bit and retry it. Do all of that with promises.
Here's a couple tools:
a promisified version of setTimeout...
function timeoutPromise(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
timeoutPromise(1000).then(() => {
console.log('time out expired');
});
A promise-returning dummy function that sometimes fails...
function fnThatMightFail() {
return new Promise((resolve, reject) => {
let fail = Math.random() < 0.40;
(fail)? reject('bad') : resolve('good');
});
}
fnThatMightFail().then(result => {
console.log(result);
}).catch(error => {
console.log(error);
});
And then, I think here's the recursive idea you're looking for. Pass in a function and a wait time between attempts, call recursively until we succeed...
function fnThatMightFail() {
return new Promise((resolve, reject) => {
let fail = Math.random() < 0.40;
(fail)? reject('bad') : resolve('good');
});
}
function timeoutPromise(ms) {
return new Promise((resolve) => {
setTimeout(() => resolve(), ms);
});
}
function fnRetryer(fn, tries, wait) {
if (tries <= 0) return Promise.reject('bad');
console.log('attempting fn');
return fn().then(result => {
console.log(`success: ${result}`);
return result;
}).catch(error => {
console.log(`error: ${error}, retrying after ${wait}ms`);
return timeoutPromise(wait).then(result => {
console.log(`${wait}ms elapsed, recursing...`);
return fnRetryer(fn, tries-1, wait);
});
});
}
fnRetryer(fnThatMightFail, 5, 1000).then(result => {
console.log(`we tried (and maybe tried) and got ${result}`);
}).catch(error => {
console.log('we failed after 5 tries, waiting 1s in between each try');
});
Note that you could add a parameter for a max number of attempts, decrement that on each recursive call and then don't recurse if that gets to zero. Also note, on the recursive call, you might opt to lengthen the wait time.

While loop with promises

What would be the idiomatic way to do something like a while loop with promises. So:
do something
if the condition still stands do it again
repeat
then do something else.
dosomething.then(possilblydomoresomethings).then(finish)
I've done it this way I was wondering if there were any better/more idomatic ways?
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')});
Output:
1
2
3
4
5
6
7
8
9
10
11
done
Here's a reusable function that I think is pretty clear.
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();
This is the simplest way I've found to express the basic pattern: you define a function that calls the promise, checks its result, and then either calls itself again or terminates.
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!'))
See it in action on JSBin
If you were using a promise that resolves or rejects, you would define then and catch instead of using an if-clause.
If you had an array of promises, you would just change loop to shift or pop the next one each time.
EDIT: Here's a version that uses async/await, because it's 2018:
const loop = async value => {
let result = null
while (result != 'ok') {
console.log(value)
result = await doSomething(value)
value = value + 1
}
console.log('yay')
}
See it in action on CodePen
As you can see, it uses a normal while loop and no recursion.
I'd use an object to wrap the value. That way you can have a done property to let the loop know you're done.
// 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');
});
This is for bluebird not q but since you didn't mention q specifically.. in the bluebird api doc the author mentions returning a promise-generating function would be more idiomatic than using deferreds.
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);
})
Since I can't comment on Stuart K's answer I'll add a little bit here. Based on Stuart K's answer you can boil it down to a surprisingly simple concept: Reuse an unfulfilled promise. What he has is essentially:
Create a new instance of a deferred promise
Define your function that you want to call in a loop
Inside that function:
Check to see if you're done; and when you are resolve the promise created in #1 and return it.
If you are not done then tell Q to use the existing promise and run the unfullfilled function that is the "recursive" function, or fail if it died. Q.when(promise, yourFunction, failFunction)
After defining your function use Q to trigger the function for the first time using Q.nextTick(yourFunction)
Finally return your new promise to the caller (which will trigger the whole thing to start).
Stuart's answer is for a more generic solution, but the basics are awesome (once you realize how it works).
This pattern is now more easily called by using q-flow. An example, for the above 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');
});
Here is an extensions to the Promise prototype to mimic the behavior of a for loop. It supports promises or immediate values for the initialization, condition, loop body, and increment sections. It also has full support for exceptions, and it does not have memory leaks. An example is given below on how to use it.
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);
});
Here is a generic solution that uses ES6 promises:
/**
* Simulates a while loop where the condition is determined by the result of a Promise.
*
* #param {Function} condition
* #param {Function} action
* #returns {Promise}
*/
function promiseWhile (condition, action) {
return new Promise((resolve, reject) => {
const loop = function () {
if (!condition()) {
resolve();
} else {
Promise.resolve(action())
.then(loop)
.catch(reject);
}
}
loop();
})
}
/**
* Simulates a do-while loop where the condition is determined by the result of a Promise.
*
* #param {Function} condition
* #param {Function} action
* #returns {Promise}
*/
function promiseDoWhile (condition, action) {
return Promise.resolve(action())
.then(() => promiseWhile(condition, action));
}
export default promiseWhile;
export {promiseWhile, promiseDoWhile};
And you can use it like this:
let myCounter = 0;
function myAsyncFunction () {
return new Promise(resolve => {
setTimeout(() => {
console.log(++myCounter);
resolve()
}, 1000)
});
}
promiseWhile(() => myCounter < 5, myAsyncFunction).then(() => console.log(`Timer completed: ${myCounter}`));
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())
I am now using this:
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);
}
This accepts an array arr and a function work and returns a Promise. The supplied function gets called once for each element in the array and gets passed the current element and it's index in the array. It may be sync or async, in which case it must return a Promise.
You can use it like this:
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);
})
Each item in the array will be handled in turn. Once all are handled, the code given to .then() will run, or, if some error occurred, the code given to .catch(). Inside the work function, you can throw an Error (in case of synchronous functions) or reject the Promise (in case of async functions) to abort the loop.
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);
})
Using the ES6 Promise, I came up with this. It chains the promises and returns a promise. It's not technically a while loop, but does show how to iterate over promises synchronously.
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.');
});
Here's my fiddle.
I thought I might as well throw my hat in the ring, using ES6 Promises...
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;
}
The executor argument is the same as that passed to a Promise constructor, but will be called repeatedly until it triggers the success callback. The before_retry function allows for custom error handling on the failed attempts. If it returns a truthy value it will be considered a form of success and the "loop" will end, with that truthy as the result. If no before_retry function is registered, or it returns a falsey value, then the loop will run for another iteration. The third option is that the before_retry function throws an error itself. If this happens, then the "loop" will end, passing that error as an error.
Here is an example:
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);
})
Output for 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
Output for 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
Output for 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
Lots of answers here and what you are trying to achieve is not very practical. but this should work. This was implemented in an aws lambda function, with Node.js 10 it will go until function timeout. It may also consume a decent amount of memory.
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');
});
}
Tested on lambda and running fine for over 5 min. But as stated by others this is not a good thing to do.
I wrote a module which helps you do chained loops of asynchronous tasks with promises, it is based on the answer above provided by 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

Resources