Sequential function calls - javascript

I have an array of asynchronous functions, it is necessary to call in order and the result of the call of the previous function is passed into the arguments. How can this be done approximately?

// lets say we have a function that takes a value and adds 20 to it asynchronously
const asyncPlus20 = num => Promise.resolve(num+a)
const arr = [asyncPlus20, asyncPlus20]
let res = 0 // some starting value
for (const f of arr) res = await f(res)
// res is now 20

One of the best way for Array of Async functions is to use For...of.
Run the below snippet in the console. >> Also includes the argument passing
const twoSecondsPromise = () => {
return new Promise((resolve) => {
setTimeout(() => resolve('2000_'), 2000);
})
};
const threeSecondsPromise = (val) => {
return new Promise((resolve) => {
setTimeout(() => resolve(val + '3000_'), 3000);
})
};
const fiveSecondsPromise = (val) => {
return new Promise((resolve) => {
setTimeout(() => resolve(val + '5000_'), 5000);
})
};
(async function () {
const asyncFunctions = [twoSecondsPromise, threeSecondsPromise, fiveSecondsPromise];
let result;
for (const file of asyncFunctions) {
result = await file(result);
console.log(result);
}
})();

Related

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 call clearTimeout and still run the setTimeout's function?

Here is my code:
let delayTimeout = null;
const delayExecution = mls => {
console.log('Delaying for', mls);
return new Promise(resolve => {
delayTimeout = setTimeout(() => resolve('ok'), mls);
})
}
const main = async () => {
axios.post('URL', {data})
.then(response => {
if(response passes some condition){
clearTimeout(delayTimeout);
}
})
const res = await delayExecution(30000)
console.log("DONE!")
}
main();
After the axios call, I may want to terminate the delayExecution by clearing the timeout inside it. How do I clearTimeout inside my delayExecution function but still resolve the Promise?
In essence, I'm trying to finish delayExecution before its time, but still resolve the promise inside it.
Based on your edit, I'll just leave another response. Note that I haven't tested it, my mind is currently focused on my code I'm writing alongside this hehe
let delayTimeout = null;
let resolveHandler = null;
const delayExecution = mls => {
console.log('Delaying for', mls);
return new Promise(resolve => {
resolveHandler = resolve;
delayTimeout = setTimeout(() => resolve('ok'), mls);
})
}
const main = async () => {
axios.post('URL', {data})
.then(response => {
if(response passes some condition){
resolveHandler('ok');
clearTimeout(delayTimeout);
}
})
const res = await delayExecution(30000)
console.log("DONE!")
}
main();
The idea is just to assign the resolve function to another auxiliary variable which you can then use elsewhere :)
doneFunc should have the clearTimeout within it, so after the function is complete the timeout is cleared.
Also, for the first setTimeout parameter, you can just pass the name of the function.
Actually for timeout, you don't need the clearTimeout since it will only be ran ONCE compared to interval which is continuing run.
const doneFunc = () => {console.log('Finished job');clearTimeout(f);}
const f = setTimeout(doneFunc, 100);
If you want to run the function independently from the timeout, just declare the function outside of it, then call it whenever you want. You have most of the code done
const doneFunc = () => console.log('Finished job');
const f = setTimeout(() => doneFunc(), 10000);
/* Seome logic here */
if (condition to run before timeout) {
clearTimeout(f);
doneFunc();
}
/* end of logic */
I have imagined that :
const runOnDelay = function( fct, delay )
{
let obj = {}
, isDone = false
, refTim = setTimeout(()=>
{
isDone = true
fct()
}, delay)
;
obj.stop = () =>
{
clearTimeout(refTim)
if (!isDone)
fct()
isDone = true
}
return obj
}
usage:
const doneFunc = () => console.log('Finished job')
let myBoy = runOnDelay(doneFunc, 1000)
//...
myBoy.stop()

how do I create a nameless async function - Nodejs

I was wondering if it's possible to create a nameless function with the quick functional notation on javascript. What I mean by changing this:
var lobby = ((io) => {
...
}
return {
getWhiteBoardId: (name2, time2, role2, pass2, need_id) => {
let promise = new Promise((res, rej) => {
setTimeout(() => res("Now it's done!"), 1000)
});
// wait until the promise returns us a value
let result = await promise;
}
})(io);
I then want to later be able to call this function:
whiteboardId = lobby.getWhiteBoardId(req.body.name, req.body.time, req.body.role, req.body.pass, need_id);
to have something at the beginning such as this:
var lobby = (async (io) => {
so that I can call my Promise and await
// defining io, to execute the code, you not need to write the next line
const io = {};
const lobby = ((io) => {
// some pre-existing code
return {
// you are using await in this method so add async to the method signature
getWhiteBoardId: async (...args) => {
let promise = new Promise((resolve, reject) => {
setTimeout(() => resolve(args), 1000)
});
// wait until the promise returns us a value
let result = await promise;
// ... some other code/logic as needed
// i have assumed you need to return the result
return result;
}
}
})(io);
// TEST: calling getWhiteBoardID method
lobby.getWhiteBoardId("Hi", "There").then(console.log);
// or
(async () => {
const res = await lobby.getWhiteBoardId("Hello World")
console.log(res);
})();
console.log("First!");

javascript: asynchronous function problems (async await with loop)

I'm having a problem with a asynchronous function in javascript
My function looks like this:
async function animate(animations) {
const promises = animations.map(async(element, index) => {
const arrayBars = document.getElementsByClassName(classes.arrayElement);
if (element.operation === 'change-color') {
const [barOneIndex, barTwoIndex] = element.positions;
const barOneStyle = arrayBars[barOneIndex].style;
const barTwoStyle = arrayBars[barTwoIndex].style;
setTimeout(() => {
barOneStyle.backgroundColor = SECONDARY_COLOR;
barTwoStyle.backgroundColor = SECONDARY_COLOR;
}, index * speed);
}
if (element.operation === 'revert-color') {
const [barOneIndex, barTwoIndex] = element.positions;
const barOneStyle = arrayBars[barOneIndex].style;
const barTwoStyle = arrayBars[barTwoIndex].style;
setTimeout(() => {
barOneStyle.backgroundColor = PRIMARY_COLOR;
barTwoStyle.backgroundColor = PRIMARY_COLOR;
}, index * speed);
}
if (element.operation === 'swap') {
setTimeout(() => {
const [barOneIndex, newHeight] = element.positions;
const barOneStyle = arrayBars[barOneIndex].style;
barOneStyle.height = `${newHeight / 1.4}px`;
}, index * speed);
}
});
await Promise.all(promises);
console.log('finished');
}
It basically animates a sorting algorithm, here's the link of the project to help you to understand easier : https://divino.dev/Sorting-algorithms-visualizer/
The problem is, I need to know when the animation ends, but everything I tried didn't wait the animations to finish.
For promises to be an array of Promises, the .map() callback needs to return Promise.
For that, you need to promisify setTimeout, for which standard practice is to write a small Promise-returning delay() function.
async function animate(animations) {
function delay(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
const promises = animations.map(async(element, index) => {
const arrayBars = document.getElementsByClassName(classes.arrayElement);
return delay(index * speed).then(() => {
if (element.operation === 'change-color') {
const [barOneIndex, barTwoIndex] = element.positions;
arrayBars[barOneIndex].style.backgroundColor = SECONDARY_COLOR;
arrayBars[barTwoIndex].style.backgroundColor = SECONDARY_COLOR;
}
if (element.operation === 'revert-color') {
const [barOneIndex, barTwoIndex] = element.positions;
arrayBars[barOneIndex].style.backgroundColor = PRIMARY_COLOR;
arrayBars[barTwoIndex].style.backgroundColor = PRIMARY_COLOR;
}
if (element.operation === 'swap') {
const [barOneIndex, newHeight] = element.positions;
arrayBars[barOneIndex].style.height = `${newHeight / 1.4}px`;
}
});
});
await Promise.all(promises);
console.log('finished');
}
Note that, since the delay is the same in all three cases, it's more economical of source code to have delay(index * speed).then(...) as an outer structure and the decision-making if(){...} if(){...} if(){...} as inner structures (or the equivalent or switch/case structure).
I see your .map() function doesn't return any promises. You can fix this with
const promises = animations.map((element, index) => new Promise((resolve, reject) => {
const arrayBars = ...
...
resolve();
}))
await Promise.all(promises);
console.log('finished');

How to access properties in async await return object

Why I can't access object properties with a . in async await return object?
I know that I can access the properties like below.
let val1 = await call(3);
let val2 = await call(4);
but I'm interested if this can be done
let v = await call(3).val + await call(4).val;
const call = (x) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
val: x
});
}, 3000)
})
}
const dummy = async () => {
//let val1 = await call(3);
//let val2 = await call(4);
//alert(value.val + val2.val);
let v = await call(3).val + await call(4).val;
alert(v);
}
dummy()
You're trying to await the value of the val property of the promise.
You need to await the promise and then read the val property from the result: (await call(3)).val
const call = (x) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
val: x
});
}, 3000)
})
}
const dummy = async () => {
let v = (await call(3)).val + (await call(4)).val;
alert(v);
}
dummy()
Just wrap the await and the expression to await in parens. Then access the property.
const call = (x) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
val: x
});
}, 3000)
})
}
const dummy = async() => {
let v = (await call(3)).val + (await call(4)).val;
alert(v);
}
dummy()
Do note that doing it this way, you're awaiting 3 seconds for the first call, then awaiting another 3 seconds for the second call. Unless the second call is somehow reliant on the first, I suggest you do the following:
const call = (x) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
val: x
});
}, 3000)
})
}
const dummy = async() => {
// Call both in parallel
let val1 = call(3);
let val2 = call(4);
// Await for both
let v = await Promise.all([val1, val2]);
// *then* add
alert(v[0].val + v[1].val);
}
dummy()
Why I can't access object properties with a . in async await return
object?
Because call returns a Promise and thus has no property val. When you await the returned Promise, the expression await call(x) will eventually resolve to {val: x}, which you can then use .val on.
Thus, you can either await each call separately and save the returned object in their own variables, or wrap your expression await call(x) in parenthesis of their own, such that you are getting the .val of the resolved value, not the promise:
const call = (x) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
val: x
});
}, 3000)
})
}
const dummy = async() => {
let v = (await call(3)).val + (await call(4)).val;
alert(v);
}
dummy()
This is because the call does not return a direct result object. It is returning a promise which will resolve value in .then callback. You can await this call.
const call = (x) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
val: x
});
}, 3000)
})
}
const dummy = async () => {
//let val1 = await call(3);
//let val2 = await call(4);
//alert(value.val + val2.val);
let v = await call(3).then(result => result.val) + await call(4).then((result)=> result.val);
alert(v);
}
dummy()

Categories

Resources