how to do Sequential API Calls in javascript? [duplicate] - javascript

var myArray = [1, 2, 3, 4, 5, 6]
function myPromise(num){
return new Promise(res => {
window.setTimeout(()=>{
res( console.log("done: " + num) )
},2000)
})
}
myPromise(myArray[0])
.then(x => myPromise(myArray[1]))
.then(x => myPromise(myArray[2]))
.then(x => myPromise(myArray[3]))
.then(x => myPromise(myArray[4]))
.then(x => myPromise(myArray[5]))
Right now, if I execute the statement above, it will run sequentially. In my actual use case the array is dynamically populated and I need to execute the myPromise() function for each member in myArray.
How can I make a "pauseable loop" that will loop for each item in the array, execute myPromise and wait for the promise to be resolved before continuing to the next iteration?

You can make the repeated application of .then into a fold pretty neatly if you’re okay with creating as many promises as array elements as is the case in the question:
myArray.reduce(
(p, x) =>
p.then(() => myPromise(x)),
Promise.resolve()
)
but given support, an async function is a better choice. It’s nicely readable and has O(1) instead of O(n) memory overhead.
const forEachSeries = async (iterable, action) => {
for (const x of iterable) {
await action(x)
}
}
forEachSeries(myArray, myPromise)
If you want to collect the return values as an array, that’s:
const mapSeries = async (iterable, fn) => {
const results = []
for (const x of iterable) {
results.push(await fn(x))
}
return results
}
or, without async function support,
const mapSeries = (iterable, fn) => {
const iterator = iterable[Symbol.iterator]()
const results = []
const go = () => {
const {value, done} = iterator.next()
if (done) {
return results
}
return fn(value).then(mapped => {
results.push(mapped)
return go()
})
}
return Promise.resolve().then(go)
}
Runnable snippet:
const myArray = [1, 2, 3, 4, 5, 6]
const sleep = ms =>
new Promise(res => {
setTimeout(res, ms)
})
const myPromise = num =>
sleep(500).then(() => {
console.log('done: ' + num)
})
const forEachSeries = async (iterable, action) => {
for (const x of iterable) {
await action(x)
}
}
forEachSeries(myArray, myPromise)
.then(() => {
console.log('all done!')
})

Don't create an array of promises. Create an array of functions returning a promise.
const f = x => new Promise(resolve => setTimeout(() => resolve(console.log(x)), 2000))
(async () => {
for (let job of [1, 2, 3, 4, 5, 6].map(x => () => f(x)))
await job()
})()
Promises start running immediately after creation. Therefore, sequential execution is ensured by constructing the next promise only after finishing the current one.

I know I am very late, and my answer is similar to what others have posted. But I thought I could post a clearer answer which may help any beginner.
Instead of using promises directly, we can use promise factory. Since the promise starts executing as soon as they are created using promise factory we delay the creation of promise.
In this example I create 5 which resolve after a second. I use a promiseCreator to create promises. Now the array promises uses promiseCreator to create 5 instances of promises. But array promiseFactories wraps promiseCreator in a function so promise is not invoked immediately. It is invoked when used.
Function executeSequentially executes all promiseLike sequentially.
When promise array is passed result is promise array itself executes parallely (actually they executed as soon as they are created, not when this line is called).
When promiseFactory array is passed result is new Promise is created when earlier promise has completed their execution.
const promiseCreator = (i, time, text) => {
return new Promise(resolve => setTimeout(
() => resolve(console.log(`${i} ${text}`)),
time)
);
}
const promises = [
promiseCreator(1, 1000, "parallel"),
promiseCreator(2, 1000, "parallel"),
promiseCreator(3, 1000, "parallel"),
promiseCreator(4, 1000, "parallel"),
promiseCreator(5, 1000, "parallel"),
]
const promiseFactories = [
() => promiseCreator(1, 1000, "sequential"),
() => promiseCreator(2, 1000, "sequential"),
() => promiseCreator(3, 1000, "sequential"),
() => promiseCreator(4, 1000, "sequential"),
() => promiseCreator(5, 1000, "sequential"),
]
function executeSequentially(promiseLikeArray) {
var result = Promise.resolve();
promiseLikeArray.forEach(function (promiseLike) {
result = result.then(promiseLike);
});
return result;
}
executeSequentially(promises)
executeSequentially(promiseFactories)

Also you can do it via recursive approach - executeSequentially calls itself:
function createPromise(x) {
return new Promise(res => {
setTimeout(() => {
console.log(x)
res(x);
}, x * 1000)
})
}
function executeSequentially(array) {
return createPromise(array.shift())
.then(x => array.length == 0 ? x : executeSequentially(array));
}
console.time('executeSequentially');
executeSequentially([1, 2, 3]).then(x => {
console.log('last value: ' + x);
console.timeEnd('executeSequentially');
});

Sequential:
you can use async await features to run promises sequentially . here's a snippet
async function chainPromiseCalls(asyncFunctions=[],respectiveParams=[]){
for(let i=0;i<asyncFunctions.length;i++){
const eachResult = await asyncFunctions[i](...respectiveParams[i]);
// do what you want to do with each result
}
return ;
}
Parallel:
for parallel you can just call each async function once in a loop , but if you do want to get their combined result , you can use Promise.all
function parallelPromiseCalls(asyncFunctions=[],respectiveParams=[]){
return Promise.all(asyncFunctions.map((func,index)=>func(...respectiveParams[index])))
.then(resultsList=>{
resultsList.forEach((result,index)=>{
// do what you want to do with each result in the list
})
return ;
})
}
note : I am considering respective parameters as an list of lists since multiple parameters should be passed to any one of the function , else if you have to pass only a single parameter to each then you can remove the spread operator.

You could use Array.reduce.
//type: [number]
var myArray = [1, 2, 3, 4, 5, 6] //doesn't really matter
//type: number -> Promise<number>
function myPromise(num){
return new Promise((resolve) => {
window.setTimeout(()=>{
resolve(console.log("done: " + num) )
},2000)
})
}
//Array.reduce has type: [a] ~> ((b, a) -> b), b) -> b
//So it can have type:
//[number] ~> ((Promise<number>, number) -> Promise<number>), Promise<number>) -> Promise<number>
//Therefore we need to give reduce a function that takes a Promise
//resolving to a number and a number which makes a new promise.
//This is the function we want:
function sequencePromises(promise, number) {
return new Promise((resolve) => {
resolve(promise.then(_ => myPromise(number)));
});
}
myArray.reduce(sequencePromises, Promise.resolve());
Of course, this simplistic approach won't work if you have a promise which can error, or if you need previous results, so you might want to make sequencePromises more generic:
function genericSequencePromises(promiseFunction) {
return (promise, parameter) => {
return new Promise((resolve, reject) =>
return promiseFunction(resolve,
reject,
promise,
parameter));
}
}
Then you can do whatever you want as long as you return a Promise.
Finally, you might benefit from this little helper:
function promiseSeries(array, reducer) {
return array.reduce(reducer, Promise.resolve());
}
Bringing it all together:
let sequencePromises = genericSequencePromises((resolve, reject, promise, num) => {
resolve(promise.then(_ => console.log(`done: ${num}`)));
}
promiseSeries(myArray, sequencePromises);
This way, you can not only handle the case in your question, but much more complex cases.

Here's a concise way using Array.reduce and async/await. Taking the eg array and myPromise function from OP:
var myArray = [1, 2, 3, 4, 5, 6];
function myPromise(num) {
return new Promise((res) => {
setTimeout(() => {
res(console.log("done: " + num));
}, 2000);
});
}
if your myPromise fn does not return result, then
myArray.reduce(async (a, b) => {
await a;
await myPromise(b);
}, null);
this can be easily modified to accumulate result. Eg, if your myPromise were to return a result, you could accumulate them in an array, in the order of myArray elements:
const resultArray = myArray.reduce(
async (a, b) => [...(await a), await myPromise(b)],
[]
);
or an object
const resultObject = myArray.reduce(async (a, b) => ({
async (a, b) => ({
...(await a),
[b]: await myPromise(b),
}),
{}
);

I would use babel and do it this way:
let args = [1, 2, 3];
const myPromise = async x => console.log('arg:',x);
const test = async () => {
for (let task of args.map(myPromise))
await task;
}
test().then(console.log('Done'));
<script src="https://unpkg.com/babel-standalone#6.24.0/babel.min.js"></script>

You can iterate over the array of elements and pass the params like this:
const arr = [1, 2, 3, 4, 5, 6]
const MyPromiseFunction = num => new Promise(
(resolve, reject) =>
// Your logic...
setTimeout(() => num <= 4
? resolve('Success!')
: reject('Rejected!'), 1000 * num)
)
const logMessage = (num, msg) =>
console.log(`For number ${num} promise result: ${msg}`)
arr.map(
async (num) => await MyPromiseFunction(num)
.then(message => logMessage(num, message))
.catch(reason => logMessage(num, reason))
)

Related

How can I run these js function in row (node.js)? [duplicate]

var myArray = [1, 2, 3, 4, 5, 6]
function myPromise(num){
return new Promise(res => {
window.setTimeout(()=>{
res( console.log("done: " + num) )
},2000)
})
}
myPromise(myArray[0])
.then(x => myPromise(myArray[1]))
.then(x => myPromise(myArray[2]))
.then(x => myPromise(myArray[3]))
.then(x => myPromise(myArray[4]))
.then(x => myPromise(myArray[5]))
Right now, if I execute the statement above, it will run sequentially. In my actual use case the array is dynamically populated and I need to execute the myPromise() function for each member in myArray.
How can I make a "pauseable loop" that will loop for each item in the array, execute myPromise and wait for the promise to be resolved before continuing to the next iteration?
You can make the repeated application of .then into a fold pretty neatly if you’re okay with creating as many promises as array elements as is the case in the question:
myArray.reduce(
(p, x) =>
p.then(() => myPromise(x)),
Promise.resolve()
)
but given support, an async function is a better choice. It’s nicely readable and has O(1) instead of O(n) memory overhead.
const forEachSeries = async (iterable, action) => {
for (const x of iterable) {
await action(x)
}
}
forEachSeries(myArray, myPromise)
If you want to collect the return values as an array, that’s:
const mapSeries = async (iterable, fn) => {
const results = []
for (const x of iterable) {
results.push(await fn(x))
}
return results
}
or, without async function support,
const mapSeries = (iterable, fn) => {
const iterator = iterable[Symbol.iterator]()
const results = []
const go = () => {
const {value, done} = iterator.next()
if (done) {
return results
}
return fn(value).then(mapped => {
results.push(mapped)
return go()
})
}
return Promise.resolve().then(go)
}
Runnable snippet:
const myArray = [1, 2, 3, 4, 5, 6]
const sleep = ms =>
new Promise(res => {
setTimeout(res, ms)
})
const myPromise = num =>
sleep(500).then(() => {
console.log('done: ' + num)
})
const forEachSeries = async (iterable, action) => {
for (const x of iterable) {
await action(x)
}
}
forEachSeries(myArray, myPromise)
.then(() => {
console.log('all done!')
})
Don't create an array of promises. Create an array of functions returning a promise.
const f = x => new Promise(resolve => setTimeout(() => resolve(console.log(x)), 2000))
(async () => {
for (let job of [1, 2, 3, 4, 5, 6].map(x => () => f(x)))
await job()
})()
Promises start running immediately after creation. Therefore, sequential execution is ensured by constructing the next promise only after finishing the current one.
I know I am very late, and my answer is similar to what others have posted. But I thought I could post a clearer answer which may help any beginner.
Instead of using promises directly, we can use promise factory. Since the promise starts executing as soon as they are created using promise factory we delay the creation of promise.
In this example I create 5 which resolve after a second. I use a promiseCreator to create promises. Now the array promises uses promiseCreator to create 5 instances of promises. But array promiseFactories wraps promiseCreator in a function so promise is not invoked immediately. It is invoked when used.
Function executeSequentially executes all promiseLike sequentially.
When promise array is passed result is promise array itself executes parallely (actually they executed as soon as they are created, not when this line is called).
When promiseFactory array is passed result is new Promise is created when earlier promise has completed their execution.
const promiseCreator = (i, time, text) => {
return new Promise(resolve => setTimeout(
() => resolve(console.log(`${i} ${text}`)),
time)
);
}
const promises = [
promiseCreator(1, 1000, "parallel"),
promiseCreator(2, 1000, "parallel"),
promiseCreator(3, 1000, "parallel"),
promiseCreator(4, 1000, "parallel"),
promiseCreator(5, 1000, "parallel"),
]
const promiseFactories = [
() => promiseCreator(1, 1000, "sequential"),
() => promiseCreator(2, 1000, "sequential"),
() => promiseCreator(3, 1000, "sequential"),
() => promiseCreator(4, 1000, "sequential"),
() => promiseCreator(5, 1000, "sequential"),
]
function executeSequentially(promiseLikeArray) {
var result = Promise.resolve();
promiseLikeArray.forEach(function (promiseLike) {
result = result.then(promiseLike);
});
return result;
}
executeSequentially(promises)
executeSequentially(promiseFactories)
Also you can do it via recursive approach - executeSequentially calls itself:
function createPromise(x) {
return new Promise(res => {
setTimeout(() => {
console.log(x)
res(x);
}, x * 1000)
})
}
function executeSequentially(array) {
return createPromise(array.shift())
.then(x => array.length == 0 ? x : executeSequentially(array));
}
console.time('executeSequentially');
executeSequentially([1, 2, 3]).then(x => {
console.log('last value: ' + x);
console.timeEnd('executeSequentially');
});
Sequential:
you can use async await features to run promises sequentially . here's a snippet
async function chainPromiseCalls(asyncFunctions=[],respectiveParams=[]){
for(let i=0;i<asyncFunctions.length;i++){
const eachResult = await asyncFunctions[i](...respectiveParams[i]);
// do what you want to do with each result
}
return ;
}
Parallel:
for parallel you can just call each async function once in a loop , but if you do want to get their combined result , you can use Promise.all
function parallelPromiseCalls(asyncFunctions=[],respectiveParams=[]){
return Promise.all(asyncFunctions.map((func,index)=>func(...respectiveParams[index])))
.then(resultsList=>{
resultsList.forEach((result,index)=>{
// do what you want to do with each result in the list
})
return ;
})
}
note : I am considering respective parameters as an list of lists since multiple parameters should be passed to any one of the function , else if you have to pass only a single parameter to each then you can remove the spread operator.
You could use Array.reduce.
//type: [number]
var myArray = [1, 2, 3, 4, 5, 6] //doesn't really matter
//type: number -> Promise<number>
function myPromise(num){
return new Promise((resolve) => {
window.setTimeout(()=>{
resolve(console.log("done: " + num) )
},2000)
})
}
//Array.reduce has type: [a] ~> ((b, a) -> b), b) -> b
//So it can have type:
//[number] ~> ((Promise<number>, number) -> Promise<number>), Promise<number>) -> Promise<number>
//Therefore we need to give reduce a function that takes a Promise
//resolving to a number and a number which makes a new promise.
//This is the function we want:
function sequencePromises(promise, number) {
return new Promise((resolve) => {
resolve(promise.then(_ => myPromise(number)));
});
}
myArray.reduce(sequencePromises, Promise.resolve());
Of course, this simplistic approach won't work if you have a promise which can error, or if you need previous results, so you might want to make sequencePromises more generic:
function genericSequencePromises(promiseFunction) {
return (promise, parameter) => {
return new Promise((resolve, reject) =>
return promiseFunction(resolve,
reject,
promise,
parameter));
}
}
Then you can do whatever you want as long as you return a Promise.
Finally, you might benefit from this little helper:
function promiseSeries(array, reducer) {
return array.reduce(reducer, Promise.resolve());
}
Bringing it all together:
let sequencePromises = genericSequencePromises((resolve, reject, promise, num) => {
resolve(promise.then(_ => console.log(`done: ${num}`)));
}
promiseSeries(myArray, sequencePromises);
This way, you can not only handle the case in your question, but much more complex cases.
Here's a concise way using Array.reduce and async/await. Taking the eg array and myPromise function from OP:
var myArray = [1, 2, 3, 4, 5, 6];
function myPromise(num) {
return new Promise((res) => {
setTimeout(() => {
res(console.log("done: " + num));
}, 2000);
});
}
if your myPromise fn does not return result, then
myArray.reduce(async (a, b) => {
await a;
await myPromise(b);
}, null);
this can be easily modified to accumulate result. Eg, if your myPromise were to return a result, you could accumulate them in an array, in the order of myArray elements:
const resultArray = myArray.reduce(
async (a, b) => [...(await a), await myPromise(b)],
[]
);
or an object
const resultObject = myArray.reduce(async (a, b) => ({
async (a, b) => ({
...(await a),
[b]: await myPromise(b),
}),
{}
);
I would use babel and do it this way:
let args = [1, 2, 3];
const myPromise = async x => console.log('arg:',x);
const test = async () => {
for (let task of args.map(myPromise))
await task;
}
test().then(console.log('Done'));
<script src="https://unpkg.com/babel-standalone#6.24.0/babel.min.js"></script>
You can iterate over the array of elements and pass the params like this:
const arr = [1, 2, 3, 4, 5, 6]
const MyPromiseFunction = num => new Promise(
(resolve, reject) =>
// Your logic...
setTimeout(() => num <= 4
? resolve('Success!')
: reject('Rejected!'), 1000 * num)
)
const logMessage = (num, msg) =>
console.log(`For number ${num} promise result: ${msg}`)
arr.map(
async (num) => await MyPromiseFunction(num)
.then(message => logMessage(num, message))
.catch(reason => logMessage(num, reason))
)

How can I filter a collection in parallel? [duplicate]

Given
let arr = [1,2,3];
function filter(num) {
return new Promise((res, rej) => {
setTimeout(() => {
if( num === 3 ) {
res(num);
} else {
rej();
}
}, 1);
});
}
function filterNums() {
return Promise.all(arr.filter(filter));
}
filterNums().then(results => {
let l = results.length;
// length should be 1, but is 3
});
The length is 3 because Promises are returned, not values. Is there a way to filter the array with a function that returns a Promise?
Note: For this example, fs.stat has been replaced with setTimeout, see https://github.com/silenceisgolden/learn-esnext/blob/array-filter-async-function/tutorials/array-filter-with-async-function.js for the specific code.
Here is a 2017 elegant solution using async/await :
Very straightforward usage:
const results = await filter(myArray, async num => {
await doAsyncStuff()
return num > 2
})
The helper function (copy this into your web page):
async function filter(arr, callback) {
const fail = Symbol()
return (await Promise.all(arr.map(async item => (await callback(item)) ? item : fail))).filter(i=>i!==fail)
}
Demo:
// Async IIFE
(async function() {
const myArray = [1, 2, 3, 4, 5]
// This is exactly what you'd expect to write
const results = await filter(myArray, async num => {
await doAsyncStuff()
return num > 2
})
console.log(results)
})()
// Arbitrary asynchronous function
function doAsyncStuff() {
return Promise.resolve()
}
// The helper function
async function filter(arr, callback) {
const fail = Symbol()
return (await Promise.all(arr.map(async item => (await callback(item)) ? item : fail))).filter(i=>i!==fail)
}
I'll even throw in a CodePen.
As mentioned in the comments, Array.prototype.filter is synchronous and therefore does not support Promises.
Since you can now (theoretically) subclass built-in types with ES6, you should be able to add your own asynchronous method which wraps the existing filter function:
Note: I've commented out the subclassing, because it's not supported by Babel just yet for Arrays
class AsyncArray /*extends Array*/ {
constructor(arr) {
this.data = arr; // In place of Array subclassing
}
filterAsync(predicate) {
// Take a copy of the array, it might mutate by the time we've finished
const data = Array.from(this.data);
// Transform all the elements into an array of promises using the predicate
// as the promise
return Promise.all(data.map((element, index) => predicate(element, index, data)))
// Use the result of the promises to call the underlying sync filter function
.then(result => {
return data.filter((element, index) => {
return result[index];
});
});
}
}
// Create an instance of your subclass instead
let arr = new AsyncArray([1,2,3,4,5]);
// Pass in your own predicate
arr.filterAsync(async (element) => {
return new Promise(res => {
setTimeout(() => {
res(element > 3);
}, 1);
});
}).then(result => {
console.log(result)
});
Babel REPL Demo
For typescript folk (or es6 just remove type syntax)
function mapAsync<T, U>(array: T[], callbackfn: (value: T, index: number, array: T[]) => Promise<U>): Promise<U[]> {
return Promise.all(array.map(callbackfn));
}
async function filterAsync<T>(array: T[], callbackfn: (value: T, index: number, array: T[]) => Promise<boolean>): Promise<T[]> {
const filterMap = await mapAsync(array, callbackfn);
return array.filter((value, index) => filterMap[index]);
}
es6
function mapAsync(array, callbackfn) {
return Promise.all(array.map(callbackfn));
}
async function filterAsync(array, callbackfn) {
const filterMap = await mapAsync(array, callbackfn);
return array.filter((value, index) => filterMap[index]);
}
es5
function mapAsync(array, callbackfn) {
return Promise.all(array.map(callbackfn));
}
function filterAsync(array, callbackfn) {
return mapAsync(array, callbackfn).then(filterMap => {
return array.filter((value, index) => filterMap[index]);
});
}
edit: demo
function mapAsync(array, callbackfn) {
return Promise.all(array.map(callbackfn));
}
function filterAsync(array, callbackfn) {
return mapAsync(array, callbackfn).then(filterMap => {
return array.filter((value, index) => filterMap[index]);
});
}
var arr = [1, 2, 3, 4];
function isThreeAsync(number) {
return new Promise((res, rej) => {
setTimeout(() => {
res(number === 3);
}, 1);
});
}
mapAsync(arr, isThreeAsync).then(result => {
console.log(result); // [ false, false, true, false ]
});
filterAsync(arr, isThreeAsync).then(result => {
console.log(result); // [ 3 ]
});
Here's a way:
var wait = ms => new Promise(resolve => setTimeout(resolve, ms));
var filter = num => wait(1).then(() => num == 3);
var filterAsync = (array, filter) =>
Promise.all(array.map(entry => filter(entry)))
.then(bits => array.filter(entry => bits.shift()));
filterAsync([1,2,3], filter)
.then(results => console.log(results.length))
.catch(e => console.error(e));
The filterAsync function takes an array and a function that must either return true or false or return a promise that resolves to true or false, what you asked for (almost, I didn't overload promise rejection because I think that's a bad idea). Let me know if you have any questions about it.
var wait = ms => new Promise(resolve => setTimeout(resolve, ms));
var filter = num => wait(1).then(() => num == 3);
var filterAsync = (array, filter) =>
Promise.all(array.map(entry => filter(entry)))
.then(bits => array.filter(entry => bits.shift()));
filterAsync([1,2,3], filter)
.then(results => console.log(results.length))
.catch(e => console.error(e));
var console = { log: msg => div.innerHTML += msg + "<br>",
error: e => console.log(e +", "+ (e.lineNumber-25)) };
<div id="div"></div>
Promise Reducer to the rescue!
[1, 2, 3, 4].reduce((op, n) => {
return op.then(filteredNs => {
return new Promise(resolve => {
setTimeout(() => {
if (n >= 3) {
console.log("Keeping", n);
resolve(filteredNs.concat(n))
} else {
console.log("Dropping", n);
resolve(filteredNs);
}
}, 1000);
});
});
}, Promise.resolve([]))
.then(filteredNs => console.log(filteredNs));
Reducers are awesome. "Reduce my problem to my goal" seems to be a pretty good strategy for anything more complex than what the simple tools will solve for you, i.e. filtering an array of things that aren't all available immediately.
asyncFilter method:
Array.prototype.asyncFilter = async function(f){
var array = this;
var booleans = await Promise.all(array.map(f));
return array.filter((x,i)=>booleans[i])
}
Late to the game but since no one else mentioned it, Bluebird supports Promise.map which is my go-to for filters requiring aysnc processing for the condition,
function filterAsync(arr) {
return Promise.map(arr, num => {
if (num === 3) return num;
})
.filter(num => num !== undefined)
}
Two lines, completely typesafe
export const asyncFilter = async <T>(list: T[], predicate: (t: T) => Promise<boolean>) => {
const resolvedPredicates = await Promise.all(list.map(predicate));
return list.filter((item, idx) => resolvedPredicates[idx]);
};
In case someone is interested in modern typescript solution (with fail symbol used for filtering):
const failSymbol = Symbol();
export async function filterAsync<T>(
itemsToFilter: T[],
filterFunction: (item: T) => Promise<boolean>,
): Promise<T[]> {
const itemsOrFailFlags = await Promise.all(
itemsToFilter.map(async (item) => {
const hasPassed = await filterFunction(item);
return hasPassed ? item : failSymbol;
}),
);
return itemsOrFailFlags.filter(
(itemOrFailFlag) => itemOrFailFlag !== failSymbol,
) as T[];
}
There is a one liner to to do that.
const filterPromise = (values, fn) =>
Promise.all(values.map(fn)).then(booleans => values.filter((_, i) => booleans[i]));
Pass the array into values and the function into fn.
More description on how this one liner works is available here.
For production purposes you probably want to use a lib like lodasync:
import { filterAsync } from 'lodasync'
const result = await filterAsync(async(element) => {
await doSomething()
return element > 3
}, array)
Under the hood, it maps your array by invoking the callback on each element and filters the array using the result. But you should not reinvent the wheel.
You can do something like this...
theArrayYouWantToFilter = await new Promise(async (resolve) => {
const tempArray = [];
theArrayYouWantToFilter.filter(async (element, index) => {
const someAsyncValue = await someAsyncFunction();
if (someAsyncValue) {
tempArray.push(someAsyncValue);
}
if (index === theArrayYouWantToFilter.length - 1) {
resolve(tempArray);
}
});
});
Wrapped within an async function...
async function filter(theArrayYouWantToFilter) {
theArrayYouWantToFilter = await new Promise(async (resolve) => {
const tempArray = [];
theArrayYouWantToFilter.filter(async (element, index) => {
const someAsyncValue = await someAsyncFunction();
if (someAsyncValue) {
tempArray.push(someAsyncValue);
}
if (index === theArrayYouWantToFilter.length - 1) {
resolve(tempArray);
}
});
});
return theArrayYouWantToFilter;
}
A valid way to do this (but it seems too messy):
let arr = [1,2,3];
function filter(num) {
return new Promise((res, rej) => {
setTimeout(() => {
if( num === 3 ) {
res(num);
} else {
rej();
}
}, 1);
});
}
async function check(num) {
try {
await filter(num);
return true;
} catch(err) {
return false;
}
}
(async function() {
for( let num of arr ) {
let res = await check(num);
if(!res) {
let index = arr.indexOf(num);
arr.splice(index, 1);
}
}
})();
Again, seems way too messy.
A variant of #DanRoss's:
async function filterNums(arr) {
return await arr.reduce(async (res, val) => {
res = await res
if (await filter(val)) {
res.push(val)
}
return res
}, Promise.resolve([]))
}
Note that if (as in current case) you don't have to worry about filter() having
side effects that need to be serialized, you can also do:
async function filterNums(arr) {
return await arr.reduce(async (res, val) => {
if (await filter(val)) {
(await res).push(val)
}
return res
}, Promise.resolve([]))
}
Late to the party, and I know that my answer is similar to other already posted answers, but the function I'm going to share is ready for be dropped into any code and be used.
As usual, when you have to do complex operations on arrays, reduce is king:
const filterAsync = (asyncPred) => arr =>
arr.reduce(async (acc,item) => {
const pass = await asyncPred(item);
if(pass) (await acc).push(item);
return acc;
},[]);
It uses modern syntax so make sure your target supports it. To be 100% correct you should use Promise.resolve([]) as the initial value, but JS just doesn't care and this way it is way shorter.
Then you can use it like this:
var wait = ms => new Promise(resolve => setTimeout(resolve, ms));
const isOdd = x => wait(1).then(()=>x%2);
(filterAsync(isOdd)([1,2,3,4,4])).then(console.log) // => [1,3]
Here's a shorter version of #pie6k's Typescript version:
async function filter<T>(arr: T[], callback: (val: T) => Promise<Boolean>) {
const fail = Symbol()
const result = (await Promise.all(arr.map(async item => (await callback(item)) ? item : fail))).filter(i => i !== fail)
return result as T[] // the "fail" entries are all filtered out so this is OK
}
An efficient way of approaching this is by processing arrays as iterables, so you can apply any number of required operations in a single iteration.
The example below uses library iter-ops for that:
import {pipe, filter, toAsync} from 'iter-ops';
const arr = [1, 2, 3]; // synchronous iterable
const i = pipe(
toAsync(arr), // make our iterable asynchronous
filter(async (value, index) => {
// returns Promise<boolean>
})
);
(async function() {
for await (const a of i) {
console.log(a); // print values
}
})();
All operators within the library support asynchronous predicates when inside an asynchronous pipeline (why we use toAsync), and you can add other operators, in the same way.
Use of Promise.all for this is quite inefficient, because you block the entire array from any further processing that can be done concurrently, which the above approach allows.

How to use reduce in a Promise.all instead of map

How can I use reduce in the place of map when using Promise.all? My attempt results in an error UnhandledPromiseRejectionWarning: TypeError: #<Promise> is not iterable at Function.all (<anonymous>)
Eventually I would like to conditionally add innerResult to memo but I need to use reduce first.
const _ = require('lodash');
const eq = [{id:1}, {id:2}, {id:3}];
// block to replace
var biggerEq = _.map(eq, async (e) => {
const innerResult = await wait(e.id);
return innerResult;
})
// attempt at replacing above block
// var biggerEq = _.reduce(eq, async (memo, e) => {
// const innerResult = await wait(e.id);
// memo.push(innerResult)
// return memo;
// }, []);
Promise.all(biggerEq).then((result) => {
console.log(result) // outputs [ 2, 4, 6 ]
})
function wait (id) {
return new Promise((resolve) => {
setTimeout(() => {
resolve(id * 2);
}, 1000);
})
}
If you want to replace it with reduce, it's possible, but the logic will be a bit convoluted. Make the accumulator a Promise that resolves to an array that you can push to, then return it so the next iteration can use it (as a Promise):
const eq = [{id:1}, {id:2}, {id:3}];
function wait (id) {
return new Promise((resolve) => {
setTimeout(() => {
resolve(id * 2);
}, 1000);
})
}
const biggerEq = _.reduce(eq, async (arrProm, obj) => {
const [arr, innerResult] = await Promise.all([arrProm, wait(obj.id)]);
arr.push(innerResult);
return arr;
}, Promise.resolve([]));
biggerEq.then((arr) => {
console.log(arr);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>
(but .map is really more appropriate when you want to transform one array into another)
I think CertainPerformance over-complicated it. You can use reduce like this with Promise.all:
const eq = [{id: 1}, {id: 2}, {id: 3}];
function wait(id) {
return new Promise((resolve) => {
setTimeout(() => {
resolve(id * 2);
}, 1000);
});
}
const biggerEq = _.reduce(eq, (arr, obj) => {
const p = wait(obj.id);
arr.push(p);
return arr;
}, []);
Promise.all(biggerEq).then((arr) => {
console.log(arr);
});
Note that the problem was with using await inside the reduce. That meant that you were pushing the results of the promises into the array, not getting an array of promises. Using that method, you already have your results in the array, so there's no need for Promise.all, but there's the big disadvantage that the promises are resolved consecutively. If that's actually what you want, you can have:
const results = _.reduce(eq, async (arr, obj) => {
const p = await wait(obj.id);
arr.push(p);
return arr;
}, []);
console.log(results);

Make ForEach Finish Before Each Loop

This piece of code waits for 2s and then console.log's each element straight away. How can I change this forEach to wait for every timeout to finish before looping over to the next one? So what I want is for 2s to go by, then it logs "1", then another 2s goes by, then it logs "2" etc. I looked at promises (half implemented here) but I don't understand how it works.
MyArray = [1, 2, 3];
MyArray.forEach(async element => {
await setTimeout(() => {
console.log(element);
}, 2000);
});
Async does not work with setTimeout, you can return a promise that resolves later and when that resolves return the next value and so on. Reduce can do this for you:
const MyArray = [1, 2, 3];
const later = (howLong, element) =>
new Promise(
resolve =>
setTimeout(() => {
console.log(element);
resolve(element)
}, howLong)
);
MyArray.reduce(
(all, element) =>
all.then(
() => later(2000, element)
),
Promise.resolve()
).then(()=>console.log("done"));
The following is in parallel:
const MyArray = [1, 2, 3];
Promise.all(
MyArray.map(element =>
new Promise(
resolve=>
setTimeout(() => {
console.log(element);
resolve(element)
}, 2000)
)
)
).then(
results=>console.log("results",results)
)
You may also use a simple recursive asynchronous sequencer as follows;
var sequenceAsync = ([d,...ds]) => d !== void 0 && asyncTask(d).then(v => (console.log(v), sequenceAsync(ds))),
asyncTask = n => new Promise(v => setTimeout(v, 1000, n)),
datas = [1,2,3];
sequenceAsync(datas);
void 0 is a perfect undefined value to check for undefined.
You are looking to run the promises in series, where each promise will resolve after 2 seconds and then next one will run
MyArray.reduce(function(chain, element) {
return chain.then(function() {
return new Promise(function(resolve, reject) {
setTimeout(() => {
console.log(element);
resolve(element)
}, 2000);
});
})
}, Promise.resolve([]))
Try this
function resolveAfter2Seconds(i) {
return new Promise(resolve => {
setTimeout(() => {
resolve(i);
}, 2000);
});
}
async function asyncCall(MyArray) {
for(var i=0; i<MyArray.length; i++){
var result = await resolveAfter2Seconds(MyArray[i]);
console.log(result);
}
}
var MyArray = [1, 2, 3];
asyncCall(MyArray);

How to execute promises sequentially, passing the parameters from an array?

var myArray = [1, 2, 3, 4, 5, 6]
function myPromise(num){
return new Promise(res => {
window.setTimeout(()=>{
res( console.log("done: " + num) )
},2000)
})
}
myPromise(myArray[0])
.then(x => myPromise(myArray[1]))
.then(x => myPromise(myArray[2]))
.then(x => myPromise(myArray[3]))
.then(x => myPromise(myArray[4]))
.then(x => myPromise(myArray[5]))
Right now, if I execute the statement above, it will run sequentially. In my actual use case the array is dynamically populated and I need to execute the myPromise() function for each member in myArray.
How can I make a "pauseable loop" that will loop for each item in the array, execute myPromise and wait for the promise to be resolved before continuing to the next iteration?
You can make the repeated application of .then into a fold pretty neatly if you’re okay with creating as many promises as array elements as is the case in the question:
myArray.reduce(
(p, x) =>
p.then(() => myPromise(x)),
Promise.resolve()
)
but given support, an async function is a better choice. It’s nicely readable and has O(1) instead of O(n) memory overhead.
const forEachSeries = async (iterable, action) => {
for (const x of iterable) {
await action(x)
}
}
forEachSeries(myArray, myPromise)
If you want to collect the return values as an array, that’s:
const mapSeries = async (iterable, fn) => {
const results = []
for (const x of iterable) {
results.push(await fn(x))
}
return results
}
or, without async function support,
const mapSeries = (iterable, fn) => {
const iterator = iterable[Symbol.iterator]()
const results = []
const go = () => {
const {value, done} = iterator.next()
if (done) {
return results
}
return fn(value).then(mapped => {
results.push(mapped)
return go()
})
}
return Promise.resolve().then(go)
}
Runnable snippet:
const myArray = [1, 2, 3, 4, 5, 6]
const sleep = ms =>
new Promise(res => {
setTimeout(res, ms)
})
const myPromise = num =>
sleep(500).then(() => {
console.log('done: ' + num)
})
const forEachSeries = async (iterable, action) => {
for (const x of iterable) {
await action(x)
}
}
forEachSeries(myArray, myPromise)
.then(() => {
console.log('all done!')
})
Don't create an array of promises. Create an array of functions returning a promise.
const f = x => new Promise(resolve => setTimeout(() => resolve(console.log(x)), 2000))
(async () => {
for (let job of [1, 2, 3, 4, 5, 6].map(x => () => f(x)))
await job()
})()
Promises start running immediately after creation. Therefore, sequential execution is ensured by constructing the next promise only after finishing the current one.
I know I am very late, and my answer is similar to what others have posted. But I thought I could post a clearer answer which may help any beginner.
Instead of using promises directly, we can use promise factory. Since the promise starts executing as soon as they are created using promise factory we delay the creation of promise.
In this example I create 5 which resolve after a second. I use a promiseCreator to create promises. Now the array promises uses promiseCreator to create 5 instances of promises. But array promiseFactories wraps promiseCreator in a function so promise is not invoked immediately. It is invoked when used.
Function executeSequentially executes all promiseLike sequentially.
When promise array is passed result is promise array itself executes parallely (actually they executed as soon as they are created, not when this line is called).
When promiseFactory array is passed result is new Promise is created when earlier promise has completed their execution.
const promiseCreator = (i, time, text) => {
return new Promise(resolve => setTimeout(
() => resolve(console.log(`${i} ${text}`)),
time)
);
}
const promises = [
promiseCreator(1, 1000, "parallel"),
promiseCreator(2, 1000, "parallel"),
promiseCreator(3, 1000, "parallel"),
promiseCreator(4, 1000, "parallel"),
promiseCreator(5, 1000, "parallel"),
]
const promiseFactories = [
() => promiseCreator(1, 1000, "sequential"),
() => promiseCreator(2, 1000, "sequential"),
() => promiseCreator(3, 1000, "sequential"),
() => promiseCreator(4, 1000, "sequential"),
() => promiseCreator(5, 1000, "sequential"),
]
function executeSequentially(promiseLikeArray) {
var result = Promise.resolve();
promiseLikeArray.forEach(function (promiseLike) {
result = result.then(promiseLike);
});
return result;
}
executeSequentially(promises)
executeSequentially(promiseFactories)
Also you can do it via recursive approach - executeSequentially calls itself:
function createPromise(x) {
return new Promise(res => {
setTimeout(() => {
console.log(x)
res(x);
}, x * 1000)
})
}
function executeSequentially(array) {
return createPromise(array.shift())
.then(x => array.length == 0 ? x : executeSequentially(array));
}
console.time('executeSequentially');
executeSequentially([1, 2, 3]).then(x => {
console.log('last value: ' + x);
console.timeEnd('executeSequentially');
});
Sequential:
you can use async await features to run promises sequentially . here's a snippet
async function chainPromiseCalls(asyncFunctions=[],respectiveParams=[]){
for(let i=0;i<asyncFunctions.length;i++){
const eachResult = await asyncFunctions[i](...respectiveParams[i]);
// do what you want to do with each result
}
return ;
}
Parallel:
for parallel you can just call each async function once in a loop , but if you do want to get their combined result , you can use Promise.all
function parallelPromiseCalls(asyncFunctions=[],respectiveParams=[]){
return Promise.all(asyncFunctions.map((func,index)=>func(...respectiveParams[index])))
.then(resultsList=>{
resultsList.forEach((result,index)=>{
// do what you want to do with each result in the list
})
return ;
})
}
note : I am considering respective parameters as an list of lists since multiple parameters should be passed to any one of the function , else if you have to pass only a single parameter to each then you can remove the spread operator.
You could use Array.reduce.
//type: [number]
var myArray = [1, 2, 3, 4, 5, 6] //doesn't really matter
//type: number -> Promise<number>
function myPromise(num){
return new Promise((resolve) => {
window.setTimeout(()=>{
resolve(console.log("done: " + num) )
},2000)
})
}
//Array.reduce has type: [a] ~> ((b, a) -> b), b) -> b
//So it can have type:
//[number] ~> ((Promise<number>, number) -> Promise<number>), Promise<number>) -> Promise<number>
//Therefore we need to give reduce a function that takes a Promise
//resolving to a number and a number which makes a new promise.
//This is the function we want:
function sequencePromises(promise, number) {
return new Promise((resolve) => {
resolve(promise.then(_ => myPromise(number)));
});
}
myArray.reduce(sequencePromises, Promise.resolve());
Of course, this simplistic approach won't work if you have a promise which can error, or if you need previous results, so you might want to make sequencePromises more generic:
function genericSequencePromises(promiseFunction) {
return (promise, parameter) => {
return new Promise((resolve, reject) =>
return promiseFunction(resolve,
reject,
promise,
parameter));
}
}
Then you can do whatever you want as long as you return a Promise.
Finally, you might benefit from this little helper:
function promiseSeries(array, reducer) {
return array.reduce(reducer, Promise.resolve());
}
Bringing it all together:
let sequencePromises = genericSequencePromises((resolve, reject, promise, num) => {
resolve(promise.then(_ => console.log(`done: ${num}`)));
}
promiseSeries(myArray, sequencePromises);
This way, you can not only handle the case in your question, but much more complex cases.
Here's a concise way using Array.reduce and async/await. Taking the eg array and myPromise function from OP:
var myArray = [1, 2, 3, 4, 5, 6];
function myPromise(num) {
return new Promise((res) => {
setTimeout(() => {
res(console.log("done: " + num));
}, 2000);
});
}
if your myPromise fn does not return result, then
myArray.reduce(async (a, b) => {
await a;
await myPromise(b);
}, null);
this can be easily modified to accumulate result. Eg, if your myPromise were to return a result, you could accumulate them in an array, in the order of myArray elements:
const resultArray = myArray.reduce(
async (a, b) => [...(await a), await myPromise(b)],
[]
);
or an object
const resultObject = myArray.reduce(async (a, b) => ({
async (a, b) => ({
...(await a),
[b]: await myPromise(b),
}),
{}
);
I would use babel and do it this way:
let args = [1, 2, 3];
const myPromise = async x => console.log('arg:',x);
const test = async () => {
for (let task of args.map(myPromise))
await task;
}
test().then(console.log('Done'));
<script src="https://unpkg.com/babel-standalone#6.24.0/babel.min.js"></script>
You can iterate over the array of elements and pass the params like this:
const arr = [1, 2, 3, 4, 5, 6]
const MyPromiseFunction = num => new Promise(
(resolve, reject) =>
// Your logic...
setTimeout(() => num <= 4
? resolve('Success!')
: reject('Rejected!'), 1000 * num)
)
const logMessage = (num, msg) =>
console.log(`For number ${num} promise result: ${msg}`)
arr.map(
async (num) => await MyPromiseFunction(num)
.then(message => logMessage(num, message))
.catch(reason => logMessage(num, reason))
)

Categories

Resources