While loop with promises - node.js

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

Related

How to use Await Inside Array.map for API's response [duplicate]

Consider the following code that reads an array of files in a serial/sequential manner. readFiles returns a promise, which is resolved only once all files have been read in sequence.
var readFile = function(file) {
... // Returns a promise.
};
var readFiles = function(files) {
return new Promise((resolve, reject) => {
var readSequential = function(index) {
if (index >= files.length) {
resolve();
} else {
readFile(files[index]).then(function() {
readSequential(index + 1);
}).catch(reject);
}
};
readSequential(0); // Start with the first file!
});
};
The above code works, but I don't like having to do recursion for things to occur sequentially. Is there a simpler way that this code can be re-written so that I don't have to use my weird readSequential function?
Originally I tried to use Promise.all, but that caused all of the readFile calls to happen concurrently, which is not what I want:
var readFiles = function(files) {
return Promise.all(files.map(function(file) {
return readFile(file);
}));
};
Update 2017: I would use an async function if the environment supports it:
async function readFiles(files) {
for(const file of files) {
await readFile(file);
}
};
If you'd like, you can defer reading the files until you need them using an async generator (if your environment supports it):
async function* readFiles(files) {
for(const file of files) {
yield await readFile(file);
}
};
Update: In second thought - I might use a for loop instead:
var readFiles = function(files) {
var p = Promise.resolve(); // Q() in q
files.forEach(file =>
p = p.then(() => readFile(file));
);
return p;
};
Or more compactly, with reduce:
var readFiles = function(files) {
return files.reduce((p, file) => {
return p.then(() => readFile(file));
}, Promise.resolve()); // initial
};
In other promise libraries (like when and Bluebird) you have utility methods for this.
For example, Bluebird would be:
var Promise = require("bluebird");
var fs = Promise.promisifyAll(require("fs"));
var readAll = Promise.resolve(files).map(fs.readFileAsync,{concurrency: 1 });
// if the order matters, you can use Promise.each instead and omit concurrency param
readAll.then(function(allFileContents){
// do stuff to read files.
});
Although there is really no reason not to use async await today.
Here is how I prefer to run tasks in series.
function runSerial() {
var that = this;
// task1 is a function that returns a promise (and immediately starts executing)
// task2 is a function that returns a promise (and immediately starts executing)
return Promise.resolve()
.then(function() {
return that.task1();
})
.then(function() {
return that.task2();
})
.then(function() {
console.log(" ---- done ----");
});
}
What about cases with more tasks? Like, 10?
function runSerial(tasks) {
var result = Promise.resolve();
tasks.forEach(task => {
result = result.then(() => task());
});
return result;
}
This question is old, but we live in a world of ES6 and functional JavaScript, so let's see how we can improve.
Because promises execute immediately, we can't just create an array of promises, they would all fire off in parallel.
Instead, we need to create an array of functions that returns a promise. Each function will then be executed sequentially, which then starts the promise inside.
We can solve this a few ways, but my favorite way is to use reduce.
It gets a little tricky using reduce in combination with promises, so I have broken down the one liner into some smaller digestible bites below.
The essence of this function is to use reduce starting with an initial value of Promise.resolve([]), or a promise containing an empty array.
This promise will then be passed into the reduce method as promise. This is the key to chaining each promise together sequentially. The next promise to execute is func and when the then fires, the results are concatenated and that promise is then returned, executing the reduce cycle with the next promise function.
Once all promises have executed, the returned promise will contain an array of all the results of each promise.
ES6 Example (one liner)
/*
* serial executes Promises sequentially.
* #param {funcs} An array of funcs that return promises.
* #example
* const urls = ['/url1', '/url2', '/url3']
* serial(urls.map(url => () => $.ajax(url)))
* .then(console.log.bind(console))
*/
const serial = funcs =>
funcs.reduce((promise, func) =>
promise.then(result => func().then(Array.prototype.concat.bind(result))), Promise.resolve([]))
ES6 Example (broken down)
// broken down to for easier understanding
const concat = list => Array.prototype.concat.bind(list)
const promiseConcat = f => x => f().then(concat(x))
const promiseReduce = (acc, x) => acc.then(promiseConcat(x))
/*
* serial executes Promises sequentially.
* #param {funcs} An array of funcs that return promises.
* #example
* const urls = ['/url1', '/url2', '/url3']
* serial(urls.map(url => () => $.ajax(url)))
* .then(console.log.bind(console))
*/
const serial = funcs => funcs.reduce(promiseReduce, Promise.resolve([]))
Usage:
// first take your work
const urls = ['/url1', '/url2', '/url3', '/url4']
// next convert each item to a function that returns a promise
const funcs = urls.map(url => () => $.ajax(url))
// execute them serially
serial(funcs)
.then(console.log.bind(console))
To do this simply in ES6:
function(files) {
// Create a new empty promise (don't do that with real people ;)
var sequence = Promise.resolve();
// Loop over each file, and add on a promise to the
// end of the 'sequence' promise.
files.forEach(file => {
// Chain one computation onto the sequence
sequence =
sequence
.then(() => performComputation(file))
.then(result => doSomething(result));
// Resolves for each file, one at a time.
})
// This will resolve after the entire chain is resolved
return sequence;
}
Addition example
const addTwo = async () => 2;
const addThree = async (inValue) => new Promise((resolve) => setTimeout(resolve(inValue + 3), 2000));
const addFour = (inValue) => new Promise((res) => setTimeout(res(inValue + 4), 1000));
const addFive = async (inValue) => inValue + 5;
// Function which handles promises from above
async function sequenceAddition() {
let sum = await [addTwo, addThree, addFour, addFive].reduce(
(promise, currPromise) => promise.then((val) => currPromise(val)),
Promise.resolve()
);
console.log('sum:', sum); // 2 + 3 + 4 + 5 = 14
}
// Run function. See console for result.
sequenceAddition();
General syntax to use reduce()
function sequence(tasks, fn) {
return tasks.reduce((promise, task) => promise.then(() => fn(task)), Promise.resolve());
}
UPDATE
items-promise is a ready to use NPM package doing the same.
I've had to run a lot of sequential tasks and used these answers to forge a function that would take care of handling any sequential task...
function one_by_one(objects_array, iterator, callback) {
var start_promise = objects_array.reduce(function (prom, object) {
return prom.then(function () {
return iterator(object);
});
}, Promise.resolve()); // initial
if(callback){
start_promise.then(callback);
}else{
return start_promise;
}
}
The function takes 2 arguments + 1 optional. First argument is the array on which we will be working. The second argument is the task itself, a function that returns a promise, the next task will be started only when this promise resolves. The third argument is a callback to run when all tasks have been done. If no callback is passed, then the function returns the promise it created so we can handle the end.
Here's an example of usage:
var filenames = ['1.jpg','2.jpg','3.jpg'];
var resize_task = function(filename){
//return promise of async resizing with filename
};
one_by_one(filenames,resize_task );
Hope it saves someone some time...
With Async/Await (if you have the support of ES7)
function downloadFile(fileUrl) { ... } // This function return a Promise
async function main()
{
var filesList = [...];
for (const file of filesList) {
await downloadFile(file);
}
}
(you must use for loop, and not forEach because async/await has problems running in forEach loop)
Without Async/Await (using Promise)
function downloadFile(fileUrl) { ... } // This function return a Promise
function downloadRecursion(filesList, index)
{
index = index || 0;
if (index < filesList.length)
{
downloadFile(filesList[index]).then(function()
{
index++;
downloadRecursion(filesList, index); // self invocation - recursion!
});
}
else
{
return Promise.resolve();
}
}
function main()
{
var filesList = [...];
downloadRecursion(filesList);
}
My preferred solution:
function processArray(arr, fn) {
return arr.reduce(
(p, v) => p.then((a) => fn(v).then(r => a.concat([r]))),
Promise.resolve([])
);
}
It's not fundamentally different from others published here but:
Applies the function to items in series
Resolves to an array of results
Doesn't require async/await (support is still quite limited, circa 2017)
Uses arrow functions; nice and concise
Example usage:
const numbers = [0, 4, 20, 100];
const multiplyBy3 = (x) => new Promise(res => res(x * 3));
// Prints [ 0, 12, 60, 300 ]
processArray(numbers, multiplyBy3).then(console.log);
Tested on reasonable current Chrome (v59) and NodeJS (v8.1.2).
First, you need to understand that a promise is executed at the time of creation.
So for example if you have a code:
["a","b","c"].map(x => returnsPromise(x))
You need to change it to:
["a","b","c"].map(x => () => returnsPromise(x))
Then we need to sequentially chain promises:
["a", "b", "c"].map(x => () => returnsPromise(x))
.reduce(
(before, after) => before.then(_ => after()),
Promise.resolve()
)
executing after(), will make sure that promise is created (and executed) only when its time comes.
Nicest solution that I was able to figure out was with bluebird promises. You can just do Promise.resolve(files).each(fs.readFileAsync); which guarantees that promises are resolved sequentially in order.
With async/await of ES2016 (and maybe some features of ES2018), this can be reduced to this form:
function readFile(file) {
... // Returns a promise.
}
async function readFiles(files) {
for (file in files) {
await readFile(file)
}
}
I haven't seen another answer express that simplicity. The OP said parallel execution of readFile was not desired. However, with IO like this it really makes sense to not be blocking on a single file read, while keeping the loop execution synchronous (you don't want to do the next step until all files have been read). Since I just learned about this and am a bit excited about it, I'll share that approach of parallel asynchronous execution of readFile with overall synchronous execution of readFiles.
async function readFiles(files) {
await Promise.all(files.map(readFile))
}
Isn't that a thing of beauty?
This is a slight variation of another answer above. Using native Promises:
function inSequence(tasks) {
return tasks.reduce((p, task) => p.then(task), Promise.resolve())
}
Explanation
If you have these tasks [t1, t2, t3], then the above is equivalent to Promise.resolve().then(t1).then(t2).then(t3). It's the behavior of reduce.
How to use
First You need to construct a list of tasks! A task is a function that accepts no argument. If you need to pass arguments to your function, then use bind or other methods to create a task. For example:
var tasks = files.map(file => processFile.bind(null, file))
inSequence(tasks).then(...)
I created this simple method on the Promise object:
Create and add a Promise.sequence method to the Promise object
Promise.sequence = function (chain) {
var results = [];
var entries = chain;
if (entries.entries) entries = entries.entries();
return new Promise(function (yes, no) {
var next = function () {
var entry = entries.next();
if(entry.done) yes(results);
else {
results.push(entry.value[1]().then(next, function() { no(results); } ));
}
};
next();
});
};
Usage:
var todo = [];
todo.push(firstPromise);
if (someCriterium) todo.push(optionalPromise);
todo.push(lastPromise);
// Invoking them
Promise.sequence(todo)
.then(function(results) {}, function(results) {});
The best thing about this extension to the Promise object, is that it is consistent with the style of promises. Promise.all and Promise.sequence is invoked the same way, but have different semantics.
Caution
Sequential running of promises is not usually a very good way to use promises. It's usually better to use Promise.all, and let the browser run the code as fast as possible. However, there are real use cases for it - for example when writing a mobile app using javascript.
My answer based on https://stackoverflow.com/a/31070150/7542429.
Promise.series = function series(arrayOfPromises) {
var results = [];
return arrayOfPromises.reduce(function(seriesPromise, promise) {
return seriesPromise.then(function() {
return promise
.then(function(result) {
results.push(result);
});
});
}, Promise.resolve())
.then(function() {
return results;
});
};
This solution returns the results as an array like Promise.all().
Usage:
Promise.series([array of promises])
.then(function(results) {
// do stuff with results here
});
Use Array.prototype.reduce, and remember to wrap your promises in a function otherwise they will already be running!
// array of Promise providers
const providers = [
function(){
return Promise.resolve(1);
},
function(){
return Promise.resolve(2);
},
function(){
return Promise.resolve(3);
}
]
const inSeries = function(providers){
const seed = Promise.resolve(null);
return providers.reduce(function(a,b){
return a.then(b);
}, seed);
};
nice and easy...
you should be able to re-use the same seed for performance, etc.
It's important to guard against empty arrays or arrays with only 1 element when using reduce, so this technique is your best bet:
const providers = [
function(v){
return Promise.resolve(v+1);
},
function(v){
return Promise.resolve(v+2);
},
function(v){
return Promise.resolve(v+3);
}
]
const inSeries = function(providers, initialVal){
if(providers.length < 1){
return Promise.resolve(null)
}
return providers.reduce((a,b) => a.then(b), providers.shift()(initialVal));
};
and then call it like:
inSeries(providers, 1).then(v => {
console.log(v); // 7
});
Using modern ES:
const series = async (tasks) => {
const results = [];
for (const task of tasks) {
const result = await task;
results.push(result);
}
return results;
};
//...
const readFiles = await series(files.map(readFile));
Most of the answers dont include the results of ALL promises individually, so in case someone is looking for this particular behaviour, this is a possible solution using recursion.
It follows the style of Promise.all:
Returns the array of results in the .then() callback.
If some promise fails, its returned immediately in the .catch() callback.
const promiseEach = (arrayOfTasks) => {
let results = []
return new Promise((resolve, reject) => {
const resolveNext = (arrayOfTasks) => {
// If all tasks are already resolved, return the final array of results
if (arrayOfTasks.length === 0) return resolve(results)
// Extract first promise and solve it
const first = arrayOfTasks.shift()
first().then((res) => {
results.push(res)
resolveNext(arrayOfTasks)
}).catch((err) => {
reject(err)
})
}
resolveNext(arrayOfTasks)
})
}
// Lets try it 😎
const promise = (time, shouldThrowError) => new Promise((resolve, reject) => {
const timeInMs = time * 1000
setTimeout(()=>{
console.log(`Waited ${time} secs`)
if (shouldThrowError) reject(new Error('Promise failed'))
resolve(time)
}, timeInMs)
})
const tasks = [() => promise(1), () => promise(2)]
promiseEach(tasks)
.then((res) => {
console.log(res) // [1, 2]
})
// Oops some promise failed
.catch((error) => {
console.log(error)
})
Note about the tasks array declaration:
In this case is not possible to use the following notation like Promise.all would use:
const tasks = [promise(1), promise(2)]
And we have to use:
const tasks = [() => promise(1), () => promise(2)]
The reason is that JavaScript starts executing the promise immediatelly after its declared. If we use methods like Promise.all, it just checks that the state of all of them is fulfilled or rejected, but doesnt start the exection itself. Using () => promise() we stop the execution until its called.
You can use this function that gets promiseFactories List:
function executeSequentially(promiseFactories) {
var result = Promise.resolve();
promiseFactories.forEach(function (promiseFactory) {
result = result.then(promiseFactory);
});
return result;
}
Promise Factory is just simple function that returns a Promise:
function myPromiseFactory() {
return somethingThatCreatesAPromise();
}
It works because a promise factory doesn't create the promise until it's asked to. It works the same way as a then function – in fact, it's the same thing!
You don't want to operate over an array of promises at all. Per the Promise spec, as soon as a promise is created, it begins executing. So what you really want is an array of promise factories...
If you want to learn more on Promises, you should check this link:
https://pouchdb.com/2015/05/18/we-have-a-problem-with-promises.html
If you want you can use reduce to make a sequential promise, for example:
[2,3,4,5,6,7,8,9].reduce((promises, page) => {
return promises.then((page) => {
console.log(page);
return Promise.resolve(page+1);
});
}, Promise.resolve(1));
it'll always works in sequential.
I really liked #joelnet's answer, but to me, that style of coding is a little bit tough to digest, so I spent a couple of days trying to figure out how I would express the same solution in a more readable manner and this is my take, just with a different syntax and some comments.
// first take your work
const urls = ['/url1', '/url2', '/url3', '/url4']
// next convert each item to a function that returns a promise
const functions = urls.map((url) => {
// For every url we return a new function
return () => {
return new Promise((resolve) => {
// random wait in milliseconds
const randomWait = parseInt((Math.random() * 1000),10)
console.log('waiting to resolve in ms', randomWait)
setTimeout(()=>resolve({randomWait, url}),randomWait)
})
}
})
const promiseReduce = (acc, next) => {
// we wait for the accumulator to resolve it's promise
return acc.then((accResult) => {
// and then we return a new promise that will become
// the new value for the accumulator
return next().then((nextResult) => {
// that eventually will resolve to a new array containing
// the value of the two promises
return accResult.concat(nextResult)
})
})
};
// the accumulator will always be a promise that resolves to an array
const accumulator = Promise.resolve([])
// we call reduce with the reduce function and the accumulator initial value
functions.reduce(promiseReduce, accumulator)
.then((result) => {
// let's display the final value here
console.log('=== The final result ===')
console.log(result)
})
As Bergi noticed, I think the best and clear solution is use BlueBird.each, code below:
const BlueBird = require('bluebird');
BlueBird.each(files, fs.readFileAsync);
I find myself coming back to this question many times and the answers aren't exactly giving me what I need, so putting this here for anyone that needs this too.
The code below does sequential promises execution (one after another), and each round consists of multiple callings:
async function sequence(list, cb) {
const result = [];
await list.reduce(async (promise, item) => promise
.then(() => cb(item))
.then((res) => result.push(res)
), Promise.resolve());
return result;
}
Showcase:
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.15.3/axios.min.js"></script>
<script src="https://unpkg.com/#babel/standalone#7/babel.min.js"></script>
<script type="text/babel">
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function readFile(url, index) {
console.log('Running index: ', index);
// First action
const firstTime = await axios.get(url);
console.log('First API response: ', firstTime.data.activity);
// Second action
await sleep(1000);
// Third action
const secondTime = await axios.get(url);
console.log('Second API response: ', secondTime.data.activity);
// Fourth action
await sleep(1000);
return secondTime.data;
}
async function sequence(urls, fn) {
const result = [];
await urls.reduce(async (promise, url, index) => promise.then(() => fn(url, index)).then((res) => result.push(res)), Promise.resolve());
return result;
}
const urls = [
'https://www.boredapi.com/api/activity',
'https://www.boredapi.com/api/activity',
'https://www.boredapi.com/api/activity',
];
(async function init() {
const result = await sequence(urls, readFile);
console.log('result', result);
})()
</script>
I use the following code to extend the Promise object. It handles rejection of the promises and returns an array of results
Code
/*
Runs tasks in sequence and resolves a promise upon finish
tasks: an array of functions that return a promise upon call.
parameters: an array of arrays corresponding to the parameters to be passed on each function call.
context: Object to use as context to call each function. (The 'this' keyword that may be used inside the function definition)
*/
Promise.sequence = function(tasks, parameters = [], context = null) {
return new Promise((resolve, reject)=>{
var nextTask = tasks.splice(0,1)[0].apply(context, parameters[0]); //Dequeue and call the first task
var output = new Array(tasks.length + 1);
var errorFlag = false;
tasks.forEach((task, index) => {
nextTask = nextTask.then(r => {
output[index] = r;
return task.apply(context, parameters[index+1]);
}, e=>{
output[index] = e;
errorFlag = true;
return task.apply(context, parameters[index+1]);
});
});
// Last task
nextTask.then(r=>{
output[output.length - 1] = r;
if (errorFlag) reject(output); else resolve(output);
})
.catch(e=>{
output[output.length - 1] = e;
reject(output);
});
});
};
Example
function functionThatReturnsAPromise(n) {
return new Promise((resolve, reject)=>{
//Emulating real life delays, like a web request
setTimeout(()=>{
resolve(n);
}, 1000);
});
}
var arrayOfArguments = [['a'],['b'],['c'],['d']];
var arrayOfFunctions = (new Array(4)).fill(functionThatReturnsAPromise);
Promise.sequence(arrayOfFunctions, arrayOfArguments)
.then(console.log)
.catch(console.error);
Your approach is not bad, but it does have two issues: it swallows errors and it employs the Explicit Promise Construction Antipattern.
You can solve both of these issues, and make the code cleaner, while still employing the same general strategy:
var Q = require("q");
var readFile = function(file) {
... // Returns a promise.
};
var readFiles = function(files) {
var readSequential = function(index) {
if (index < files.length) {
return readFile(files[index]).then(function() {
return readSequential(index + 1);
});
}
};
// using Promise.resolve() here in case files.length is 0
return Promise.resolve(readSequential(0)); // Start!
};
This is my sequentially implementation that I use in various projects:
const file = [file1, file2, file3];
const fileContents = sequentially(readFile, files);
// somewhere else in the code:
export const sequentially = async <T, P>(
toPromise: (element: T) => Promise<P>,
elements: T[]
): Promise<P[]> => {
const results: P[] = [];
await elements.reduce(async (sequence, element) => {
await sequence;
results.push(await toPromise(element));
}, Promise.resolve());
return results;
};
Here is my Angular/TypeScript approach, using RxJS:
Given an array of URL strings, convert it into an Observable using the from function.
Use pipe to wrap the Ajax request, immediate response logic, any desired delay, and error handling.
Inside of the pipe, use concatMap to serialize the requests. Otherwise, using Javascript forEach or map would make the requests at the same time.
Use RxJS ajax to make the call, and also to add any desired delay after each call returns.
Working example: https://stackblitz.com/edit/rxjs-bnrkix?file=index.ts
The code looks like this (I left in some extras so you can choose what to keep or discard):
import { ajax } from 'rxjs/ajax';
import { catchError, concatMap, delay, from, of, map, Observable } from 'rxjs';
const urls = [
'https://randomuser.me/api/',
'https://randomuser.me/api/',
'https://randomuser.me/api/',
];
const delayAfterCall = 500;
from(urls)
.pipe(
concatMap((url: string) => {
return ajax.getJSON(url).pipe(
map((response) => {
console.log('Done! Received:', response);
return response;
}),
catchError((error) => {
console.error('Error: ', error);
return of(error);
}),
delay(delayAfterCall)
);
})
)
.subscribe((response) => {
console.log('received email:', response.results[0].email);
});
On the basis of the question's title, "Resolve promises one after another (i.e. in sequence)?", we might understand that the OP is more interested in the sequential handling of promises on settlement than sequential calls per se.
This answer is offered :
to demonstrate that sequential calls are not necessary for sequential handling of responses.
to expose viable alternative patterns to this page's visitors - including the OP if he is still interested over a year later.
despite the OP's assertion that he does not want to make calls concurrently, which may genuinely be the case but equally may be an assumption based on the desire for sequential handling of responses as the title implies.
If concurrent calls are genuinely not wanted then see Benjamin Gruenbaum's answer which covers sequential calls (etc) comprehensively.
If however, you are interested (for improved performance) in patterns which allow concurrent calls followed by sequential handling of responses, then please read on.
It's tempting to think you have to use Promise.all(arr.map(fn)).then(fn) (as I have done many times) or a Promise lib's fancy sugar (notably Bluebird's), however (with credit to this article) an arr.map(fn).reduce(fn) pattern will do the job, with the advantages that it :
works with any promise lib - even pre-compliant versions of jQuery - only .then() is used.
affords the flexibility to skip-over-error or stop-on-error, whichever you want with a one line mod.
Here it is, written for Q.
var readFiles = function(files) {
return files.map(readFile) //Make calls in parallel.
.reduce(function(sequence, filePromise) {
return sequence.then(function() {
return filePromise;
}).then(function(file) {
//Do stuff with file ... in the correct sequence!
}, function(error) {
console.log(error); //optional
return sequence;//skip-over-error. To stop-on-error, `return error` (jQuery), or `throw error` (Promises/A+).
});
}, Q()).then(function() {
// all done.
});
};
Note: only that one fragment, Q(), is specific to Q. For jQuery you need to ensure that readFile() returns a jQuery promise. With A+ libs, foreign promises will be assimilated.
The key here is the reduction's sequence promise, which sequences the handling of the readFile promises but not their creation.
And once you have absorbed that, it's maybe slightly mind-blowing when you realise that the .map() stage isn't actually necessary! The whole job, parallel calls plus serial handling in the correct order, can be achieved with reduce() alone, plus the added advantage of further flexibility to :
convert from parallel async calls to serial async calls by simply moving one line - potentially useful during development.
Here it is, for Q again.
var readFiles = function(files) {
return files.reduce(function(sequence, f) {
var filePromise = readFile(f);//Make calls in parallel. To call sequentially, move this line down one.
return sequence.then(function() {
return filePromise;
}).then(function(file) {
//Do stuff with file ... in the correct sequence!
}, function(error) {
console.log(error); //optional
return sequence;//Skip over any errors. To stop-on-error, `return error` (jQuery), or `throw error` (Promises/A+).
});
}, Q()).then(function() {
// all done.
});
};
That's the basic pattern. If you wanted also to deliver data (eg the files or some transform of them) to the caller, you would need a mild variant.
If someone else needs a guaranteed way of STRICTLY sequential way of resolving Promises when performing CRUD operations you also can use the following code as a basis.
As long as you add 'return' before calling each function, describing a Promise, and use this example as a basis the next .then() function call will CONSISTENTLY start after the completion of the previous one:
getRidOfOlderShoutsPromise = () => {
return readShoutsPromise('BEFORE')
.then(() => {
return deleteOlderShoutsPromise();
})
.then(() => {
return readShoutsPromise('AFTER')
})
.catch(err => console.log(err.message));
}
deleteOlderShoutsPromise = () => {
return new Promise ( (resolve, reject) => {
console.log("in deleteOlderShouts");
let d = new Date();
let TwoMinuteAgo = d - 1000 * 90 ;
All_Shouts.deleteMany({ dateTime: {$lt: TwoMinuteAgo}}, function(err) {
if (err) reject();
console.log("DELETED OLDs at "+d);
resolve();
});
});
}
readShoutsPromise = (tex) => {
return new Promise( (resolve, reject) => {
console.log("in readShoutsPromise -"+tex);
All_Shouts
.find({})
.sort([['dateTime', 'ascending']])
.exec(function (err, data){
if (err) reject();
let d = new Date();
console.log("shouts "+tex+" delete PROMISE = "+data.length +"; date ="+d);
resolve(data);
});
});
}
Array push and pop method can be used for sequence of promises. You can also push new promises when you need additional data. This is the code, I will use in React Infinite loader to load sequence of pages.
var promises = [Promise.resolve()];
function methodThatReturnsAPromise(page) {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(`Resolve-${page}! ${new Date()} `);
resolve();
}, 1000);
});
}
function pushPromise(page) {
promises.push(promises.pop().then(function () {
return methodThatReturnsAPromise(page)
}));
}
pushPromise(1);
pushPromise(2);
pushPromise(3);
(function() {
function sleep(ms) {
return new Promise(function(resolve) {
setTimeout(function() {
return resolve();
}, ms);
});
}
function serial(arr, index, results) {
if (index == arr.length) {
return Promise.resolve(results);
}
return new Promise(function(resolve, reject) {
if (!index) {
index = 0;
results = [];
}
return arr[index]()
.then(function(d) {
return resolve(d);
})
.catch(function(err) {
return reject(err);
});
})
.then(function(result) {
console.log("here");
results.push(result);
return serial(arr, index + 1, results);
})
.catch(function(err) {
throw err;
});
}
const a = [5000, 5000, 5000];
serial(a.map(x => () => sleep(x)));
})();
Here the key is how you call the sleep function. You need to pass an array of functions which itself returns a promise instead of an array of 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.

NodeJS constructing array from asynchronious callbacks before returning

I'm writing a function that's returning and array of values. Some of the values are calculated in a callback. But I don't know how to make the program asynchronious so all of my results are in the array, and not added after they're returned.
let array = []
for (stuff : stuffs) {
if (condition) {
array.add(stuff)
} else {
api.compute(stuff, callback(resp) {
array.add(resp.stuff)
}
}
}
res.json({ "stuff": array })
In this example the array is written to the response before the async calls have finished.
How can I make this work asynchronously?
You have to use one of the approaches:
async library
Promise.all
coroutines/generators
async/await
The most cool yet, I think, is async/await. First we modify your function, so it returns a promise:
const compute = function(stuff) {
return new Promise( (resolve, reject) => {
api.compute(stuff, callback(resp){
resolve(resp.stuff)
});
});
};
Then we modify your route with async handler:
app.get('/', async function(req, res, next) {
const array = [];
for (const stuff of stuffs) {
if (condition) {
array.add(stuff);
} else {
const stuff = await compute(stuff);
array.push(stuff);
}
}
res.json({ stuff: array });
});
Note: You might need to update node version to latest.
UPDATE:
Those who are not awared, how event loop works, execute this snippet, and finish with that:
const sleep = async function(ms) {
console.log(`Sleeping ${ms}ms`);
return new Promise( resolve => setTimeout(resolve, ms));
};
async function job() {
console.log('start');
for (let t = 0; t < 10; t++) {
await sleep(100);
}
}
job();
console.log('oops did not expect that oO');
You will be surprised.
Here is an answer without package using callbacks
Create a function that's gonna recursively treat all your stuffs.
getArray(stuffs, callback, index = 0, array = []) {
// Did we treat all stuffs?
if (stuffs.length >= index) {
return callback(array);
}
// Treat one stuff
if (condition) {
array.add(stuffs[index]);
// Call next
return getArray(stuffs, callback, index + 1, array);
}
// Get a stuff asynchronously
return api.compute(stuffs[index], (resp) => {
array.add(resp.stuff);
// Call next
return getArray(stuffs, callback, index + 1, array);
});
}
How to call it?
getArray(stuffs, (array) => {
// Here you have your array
// ...
});
EDIT: more explanation
What we want to do to transform the loop you had into a loop that handle asynchronous function call.
The purpose is that one getArray call gonna treat one index of your stuffs array.
After treating one index, the function will call itself again to treat the next index, until all get treated.
-> Treat index 0 -> Treat index 1 -> Treat index 2 -> Return all result
We are using parameters to pass the infos through the process. Index to know which array part we have to treat, and array to keep a tract of what we did calculate.
EDIT: Improvement to 100% asynchronous soluce
What we have done here it's a simple transposition of your initial for loop into an asynchronous code. it can be improved so by making it totally asynchronous, which make it better but slightly more difficult.
For example :
// Where we store the results
const array = [];
const calculationIsDone = (array) => {
// Here our calculation is done
// ---
};
// Function that's gonna aggregate the results coming asynchronously
// When we did gather all results, we call a function
const gatherCalculResult = (newResult) => {
array.push(newResult);
if (array.length === stuffs.length) {
callback(array);
}
};
// Function that makes the calculation for one stuff
const makeCalculation = (oneStuff) => {
if (condition) {
return gatherCalculResult(oneStuff);
}
// Get a stuff asynchronously
return api.compute(oneStuff, (resp) => {
gatherCalculResult(resp.stuff);
});
};
// We trigger all calculation
stuffs.forEach(x => x.makeCalculation(x));

Using promises to control flow is not working properly

I am trying to control the flow of the execution in my code below, meaning I want it to be serial.
I am reading and updating data from and to my DB, and ofc I want that to happen in the correct order. Below is the function I am calling my DB from, the queries functions are wrapped in callbacks.
I am pretty new to promises so perhaps the error might be something silly I am overlooking. If you need anything to ask please do so.
function my_function(array, array2)
{
var array3 = [];
return Promise.resolve(true)
.then(function()
{
console.log("1")
for(var i=0; i< array.length; i++)
{
get(array[i], function(results){
console.log("2")
array3.push(..);
});
}
return array3;
}).then(function()
{
console.log("3")
for(var i=0; i< array2.length; i+=2)
{
//...
get(array2[i], function(results){
console.log("4")
return array3.push(...);
});
}
return array3;
}).then(function(array3)
{
console.log("5")
for(var i=0; i<array3.length; i++)
{
get(array3[i], function(results){
console.log("6")
update(.., function(callb_result){
return;
});
});
}
});
}
And here is the way I am calling the queries.
function get(array, callback)
{
db.get(`SELECT .. FROM .. WHERE ..;`, function(error, row) {
...
return callback(something);
});
}
function update(.., callback)
{
db.run(`UPDATE .. SET ...`);
return callback("updated"); //I dont want to return anything
}
Whats printed in the log
1
3
5
2
4
6
I was thinking perhaps the way I ma calling the queries is async and that's messing up everything.
You're using for loops to run asynchronous tasks and return an array that is modified by them. But because they are asynchronous the return happens before they are finished. Instead you can create an array of promises where each promise is one of the asynchronous tasks that resolves once the task is done. To wait until every task is done you can call Promise.all with the array of promises, which returns a promise that resolves with an array of the resolved results.
For the first .then you can use Array.prototype.map to easily create an array of promises. Each item in the array needs to return a new Promise that resolves with the result from the callback of get.
.then(function() {
console.log("1");
const promiseArray = array.map(function(item) {
return new Promise(function(resolve) {
get(item, function(result) {
console.log("2");
resolve(result);
});
});
});
return Promise.all(promiseArray);
})
As you return Promise.all the next .then call be executed once all the promises in the promiseArray are fulfilled. It will receive the array of results as the first parameter to the function. That means you can use them there. The second .then is similar to the first one, except that you don't want to call get on every item. In this case map is not applicable, so the for loop will just create a promise and add it to the array of promises. Before you have used array3 to store the results that you want to update, but with promises you don't really need that. In this case you can simply concat the results of both arrays.
.then(function(resultsArray) {
console.log("3");
const promiseArray2 = [];
for (var i = 0; i < array2.length; i += 2) {
const promise = new Promise(function(resolve) {
get(array2[i], function(results) {
console.log("4");
resolve(results);
});
});
promiseArray2.push(promise);
}
// Wait for all promises to be resolved
// Then concatenate both arrays of results
return Promise.all(promiseArray2).then(function(resultsArray2) {
return resultsArray.concat(resultsArray2);
});
})
This returns a promise that resolves with the concatenated array, so you will have all the results (from both .then calls) as an array, which is passed to the next .then function. In the third and final .then you simply call update on each element of the array. You don't need to call get again, as you've already done this and you passed on the results.
.then(function(finalResults) {
console.log("5");
for (var i = 0; i < finalResults.length; i++) {
console.log("6");
update(finalResults[i], function(result) {
console.log(result);
});
}
});
Full runnable code (get uses a timeout to simulate asynchronous calls)
function myFunction(array, array2) {
return Promise.resolve(true)
.then(function() {
console.log("1");
const promiseArray = array.map(function(item) {
return new Promise(function(resolve) {
get(item, function(results) {
console.log("2");
resolve(results);
});
});
});
return Promise.all(promiseArray);
})
.then(function(resultsArray) {
console.log("3");
const promiseArray2 = [];
for (var i = 0; i < array2.length; i += 2) {
const promise = new Promise(function(resolve) {
get(array2[i], function(results) {
console.log("4");
resolve(results);
});
});
promiseArray2.push(promise);
}
return Promise.all(promiseArray2).then(function(resultsArray2) {
return resultsArray.concat(resultsArray2);
});
})
.then(function(finalResults) {
console.log("5");
for (var i = 0; i < finalResults.length; i++) {
console.log("6");
update(finalResults[i]);
}
});
}
function get(item, cb) {
// Simply call the callback with the item after 1 second
setTimeout(() => cb(item), 1000);
}
function update(item) {
// Log what item is being updated
console.log(`Updated ${item}`);
}
// Test data
const array = ["arr1item1", "arr1item2", "arr1item3"];
const array2 = ["arr2item1", "arr2item2", "arr2item3"];
myFunction(array, array2);
Improving the code
The code now works as expected, but there are many improvements that make it a lot easier to understand and conveniently also shorter.
To simplify the code you can change your get function to return a promise. This makes it a lot easier, since you don't need to create a promise in every step. And update doesn't need to be a promise, neither does it need a callback as it's synchronous.
function get(array) {
return new Promise(function(resolve, reject) {
db.get(`SELECT .. FROM .. WHERE ..;`, function(error, row) {
if (err) {
return reject(error);
}
resolve(something);
});
});
}
Now you can use get everywhere you used to create a new promise. Note: I added the reject case when there is an error, and you'll have to take care of them with a .catch on the promise.
There are still too many unnecessary .then calls. First of all Promise.resolve(true) is useless since you can just return the promise of the first .then call directly. All it did in your example was to automatically wrap the result of it in a promise.
You're also using two .then calls to create an array of the results. Not only that, but they perform exactly the same call, namely get. Currently you also wait until the first set has finished until you execute the second set, but they can be all executed at the same time. Instead you can create an array of all the get promises and then wait for all of them to finish.
function myFunction(array, array2) {
// array.map(get) is equivalent to array.map(item => get(item))
// which in turn is equivalent to:
// array.map(function(item) {
// return get(item);
// })
const promiseArray = array.map(get);
for (let i = 0; i < array2.length; i += 2) {
promiseArray.push(get(array2[i]));
}
return Promise.all(promiseArray).then(results => results.forEach(update));
}
The myFunction body has been reduced from 32 lines of code (not counting the console.log("1") etc.) to 5.
Runnable Snippet
function myFunction(array, array2) {
const promiseArray = array.map(get);
for (let i = 0; i < array2.length; i += 2) {
promiseArray.push(get(array2[i]));
}
return Promise.all(promiseArray).then(results => results.forEach(update));
}
function get(item) {
console.log(`Starting get of ${item}`);
return new Promise((resolve, reject) => {
// Simply call the callback with the item after 1 second
setTimeout(() => resolve(item), 1000);
});
}
function update(item) {
// Log what item is being updated
console.log(`Updated ${item}`);
}
// Test data
const testArr1 = ["arr1item1", "arr1item2", "arr1item3"];
const testArr2 = ["arr2item1", "arr2item2", "arr2item3"];
myFunction(testArr1, testArr2).then(() => console.log("Updated all items"));

BlueBird Promises returns "Undefined" when return inside "then" function

Here is my Code:
Checking if user is folowing official Twitter Account (here I've returned new Promise
var isFollowing = function(sender) {
return new promise(function(resolve, reject) {
var following = false;
var params = {
source_id: sender,
target_screen_name: config.get('twitter.official_account')
};
TwitObj.get('friendships/show', params, function(err, data) {
if (!err) {
following = data.relationship.source.following;
resolve(following);
} else {
reject(err);
}
});
});
};
Validation:
var validateMsg = function(dm) {
var sender = getSender(dm);
var result = [];
isFollowing(sender.id)
.then(function(response) {
if (!response) {
result = interactiveMessage(false, lang.__('FOLLOW_MAIN', sender.name));
console.log(result); // Displays as expected
return result; // Not returning value Do I need to return promise again? If yes, WHY?
}
});
};
Main Implementation:
var direct_message = function(msg) {
verifyAccount().catch(function(err) {
console.error(err);
});
var dm = msg.direct_message;
var result = validateMsg(dm);
console.log(result);
};
Questions is how should I force validateMsg function to return result variable inside then function.
Update: While debugging, I got to know that, console.log(response) in
validation function is displaying later after throwing undefined in
"then" function which means program is not able to get response
immediately and due to async nature, I/O is not getting blocked. How
to tackle this?
You're not actually returning anything in the validateMsg function. You're returning a synchronous value ( result ) in the .then function which is a separate function.
The second thing to consider is that you're expecting the validateMsg function, which is asynchronous to behave synchronously.
var result = validateMsg(dm);
The way to achieve what I believe you're looking to do involves making the following changes:
1) Return the promise chain in the validateMsg function.
var validateMsg = function(dm) {
var sender = getSender(dm);
var result = [];
return isFollowing(sender.id)
.then(function(response) {
if (!response) {
result = interactiveMessage(false, lang.__('FOLLOW_MAIN', sender.name));
return result;
}
// Not sure what you want to do here if there is a response!
// At the moment you're not doing anything if the validation
// is successful!
return response;
});
};
2) Change your function call, since you're now returning a promise.
validateMsg(dm).then( function( result ){
console.log( result );
})
3) Consider adding a catch somewhere if only to help you debug :)
validateMsg(dm).then( function( result ){
console.log( result );
})
.catch( function( err ){
console.log( "Err:::", err );
});
The validationMsg function does not have a return value, thus the result in the main function has the value undefined. Try to put return in front of isFollowing so the function returns a promise, then treat validationMsg as a promise.

Resources