First promise inside Promise.all() doesn't execute properly - javascript

the wait function is used as a sleep function, fn function takes an array (items), it logs each item and sleeps for a second before logging the next item.
const wait = async(time) => {
return new Promise((resolve) => setTimeout(resolve, time))
}
const fn = async(items) => {
for (item of items) {
await wait(1000)
console.log(item)
}
}
const exeAll = async() => {
Promise.all([
fn(['A1', 'A2']),
fn(['B1', 'B2']),
])
}
exeAll()
The problem is the result provided by exeAll function which prints:
B1
A2
B2
B2
But I think it should print something like:
A1
B1
A2
B2
A1 doesn't show at all when the above code is executed. Can anybody explain why ?

for (item of items) { will create an implicit global variable item, i.e. multiple calls to fn will interfere with each other, overwriting item. Properly declare the variable and it works as expected:
const wait = async(time) => {
return new Promise((resolve) => setTimeout(resolve, time))
}
const fn = async(items) => {
for (let item of items) {
// ^^^^
await wait(1000)
console.log(item)
}
}
const exeAll = async() => {
Promise.all([
fn(['A1', 'A2']),
fn(['B1', 'B2']),
])
}
exeAll()
We can add more logging to fn to see what happens when in your case:
const wait = async(time) => {
return new Promise((resolve) => setTimeout(resolve, time))
}
let counter = 0;
const fn = async(items) => {
const c = counter++;
console.log(`enter fn call #${c}`);
for (item of items) {
console.log(`fn call #${c} sets item <-`, item);
await wait(1000)
console.log(`fn call #${c} reads item ->`, item);
console.log(item)
}
}
const exeAll = async() => {
Promise.all([
fn(['A1', 'A2']),
fn(['B1', 'B2']),
])
}
exeAll()
Strict mode ("use strict";) would have caught that mistake because assigning to an undeclared variable throws an error.

Related

What is the usage of this code in CancelToken.js for the source code of Axios

Recently I was learning the source code of Axios(https://github.com/axios/axios).
However, in CancelToken.js, there is a part of code that I don't understand.
// eslint-disable-next-line func-names
this.promise.then = onfulfilled => {
let _resolve;
// eslint-disable-next-line func-names
const promise = new Promise(resolve => {
token.subscribe(resolve);
_resolve = resolve;
}).then(onfulfilled);
promise.cancel = function reject() {
token.unsubscribe(_resolve);
};
return promise;
};
Originally, I think it is used to overwrite the then method of this.promise. And then, I write a demo to verify my idea.
let resolve
let p = new Promise((res, rej) => resolve = res)
p.then(val => {
console.log('In p.then ', val)
})
p.then = onFulfill => {
console.log('I am here')
const promise = new Promise(resolve => {
resolve(2)
}).then(onFulfill)
return promise
}
console.log(p.then)
resolve(1)
If as what I expected, 'I am here' should be print. However, it can't print 'I am here'. So I don't understand the usage of this.promise.then = onFulfilled => ... in Axios source code.
Can anyone help me! THX!
Overwriting the method
You are correct, this does overwrite the then method of this Promise instance; the important thing here is when that happens.
If you consider this (extremely poor) resolve-only Promise implementation:
class Future {
constructor(executor) {
this._state = 'pending';
executor(result => {
if (this._state !== 'pending') {
return;
}
this._state = 'fulfilled';
if (!this._onFulfilled) {
return;
}
this._onFulfilled(result);
});
}
then(onFulfilled) {
this._onFulfilled = onFulfilled;
}
}
you can see that, if called like so:
const future = new Future(resolve => setTimeout(resolve, 1000));
future.then(() => console.log('fulfilled'));
future.then = console.log;
future.then('and then?');
reassigning then will leave the internal value of _onFulfilled untouched, and any subsequent calls to future.then will use the modified method.
In Axios?
In the Axios code, this appears to be used to allow using the CancelToken instance in two ways:
as an object owning a thenable, where the modified then appends the onFufilled handler to the internal _listeners array and returns a Promise, or
as a rough Observable, pushing the handler directly onto the _listeners array
So, mocking this up using the following setup:
let resolve;
const future = new Promise(res => resolve = res)
const listeners = [];
future.then(val => {
for (const listener of listeners) {
listener(val);
}
});
future.then = onFulfilled => new Promise(resolve => {
listeners.push(resolve);
}).then(onFulfilled);
the output of this:
for (let index = 0; index < 10; index++) {
future.then(val => console.log(`${index}: ${val}`));
}
is equivalent to that of this:
for (let index = 0; index < 10; index++) {
listeners.push(val => console.log(`${index}: ${val}`));
}
when kicked off with:
resolve(1);

Print two array one after another(with interval) in javascript

I have two array :
numbers:[1,2,3,4]
letters: ["a","b","c","d"]
I want to print as the numbers array first with time interval of 3 sec and then print letters array.
output should be: 1(3sec interval) 2(3sec interval) 3(3sec interval) 4(3sec interval) 5(3sec interval) a b c d.
I tried with following code:
const result = document.getElementById("frame")
const numbers = [1,2,3,4], letters = ["a","b","c","d"]
const function1 = () =>
letters.forEach((c, i) => setTimeout(() => console.log(c)));
const function2 = () =>
numbers.forEach((c, i) => setTimeout(() => console.log(c), i * 3000));
async function main(){
await function1();
await function2();
}
main();
const numbers = [1, 2, 3, 4]
const letters = ['a', 'b', 'c', 'd']
const wait = value => new Promise(resolve => setTimeout(() => resolve(), value))
const function1 = async () => {
numbers.forEach(async (item, i) => {
await wait(3000 * i)
console.log(item)
})
await wait(3000 * numbers.length - 1)
letters.forEach((item) => {
console.log(item)
})
}
function1()
You can do this
const numbers = [1,2,3,4], letters = ["a","b","c","d"]
const function1 = () => new Promise((resolve) => {
letters.forEach((c, i) => setTimeout(() => console.log(c), i * 3000));
setTimeout(resolve, letters.length * 3000)
})
const function2 = () =>
numbers.forEach((c, i) => setTimeout(() => console.log(c)));
async function main(){
await function1();
await function2();
}
main();
If you're just logging all the letters at once the function doesn't need to be async. You can just return the joined array from function2.
function1 does need to be async but that's not what happening in your code. Because setTimeout doesn't behave well inside async functions you need to set up a promise-based delay, and then use that inside the function.
const numbers = [1, 2, 3, 4];
const letters = ['A', 'B', 'C', 'D'];
// Resolve a promise after a given time
function delay(time) {
return new Promise(res => {
return setTimeout(() => res(), time);
});
}
function function1(arr) {
// Return a promise that is awaited
return new Promise(res => {
// Loop over the array
async function loop(i) {
// Log the element
console.log(arr[i]);
// Call the delay function
await delay(3000);
// And then work out if you need
// to call `loop` again, or resolve the promise
if (arr.length - 1 === i) {
res();
} else {
loop(++i);
}
}
// Call loop for the first time
loop(0);
});
}
function function2(arr) {
return arr.join(' ');
}
async function main() {
await function1(numbers);
console.log(function2(letters));
}
main();
For the record, another simple example where the output is awaited. This assumes your final goal of outputting will be the same for both arrays, just without delays. In case the actual output method is async, you can await that too, but keep a single entry point for both arrays
const numbers = [1,2,3,4], letters = ["a","b","c","d"];
async function output(arr,delayAfterEach){
for(element of arr){
console.log(element);
if(delayAfterEach)
await new Promise(r=>setTimeout(r,delayAfterEach));
}
}
async function main(){
await output(numbers,3000);
await output(letters);
}
main();

Override function prototype with async method, keeping this context

In javascript if i have a function defined like so
function Person() {
}
Person.prototype.someFunction = function() {
// Does soome logic
return this
}
Person.prototype.anotherFunction = function () {
// Does soome logic
return this;
};
And i want to implement chaining i will do something like this
const person = new Person();
person.someFunction().anotherFunction()
And this works as each method returns the instance of Person.
Now if i have a method which has some async action how do i return the this instsance in an async method
function someApiCall() {
return new Promise((res) => {
setTimeout(() => {
res('got data');
}, 2000);
});
}
Person.prototype.asyncFunction = function () {
someApiCall()
.then()
.catch()
// HOW DO I RETURN THIS INSTANCE HERE ???? as someAPICALL is async
};
So that i can use it as
person.someFunction().asyncFunction().anotherFunction()
Option 1 (Not executed in order):
Person.prototype.asyncFunction1 = function () {
someApiCall()
.then((e) => console.log(e))
.catch()
return this;
};
p.anotherFunction().asyncFunction1().anotherFunction()
All functions get called, but not in order. If you want to execute it in order, just do it like this:
Option 2 (Executed in order):
Person.prototype.asyncFunction2 = async function () {
const ans = await someApiCall();
console.log(ans);
return this;
};
// t represents this as you return it in asyncFunction2
p.anotherFunction().asyncFunction2().then((t) => t.anotherFunction())
You're trying to apply a synchronous programming paradigm to an asynchronous code approach, and that's just not going to work. When working with promises, which is what async does for you in an automatic way, rather than instance chaining, your code logic now needs to deal with promise chaining instead.
First, let's stop using legacy prototype syntax and look at modern (where "modern" is over five years old by now) class syntax:
class Person {
async someFunction() {
return ...
}
async anotherFunction() {
return ...
}
}
Because async is just a convenient promise wrapping, we have two options:
const person = new Person();
person
.someFunction()
.then(result => {
person
.anotherFunction()
.then(result => ...);
.catch(e => console.error(e));
})
.catch(e => console.error(e));
but this is both cumbersome and ugly. Let's use awaits instead:
const person = new Person();
try {
const someResult = await person.someFunction();
const anotherResult = await person..anotherFunction();
...
} catch (e) {
console.error(e);
}
Much better. We don't need instance chaining anymore when we're using async patterns, it's a pattern from a previous era of JS, and writing modern code does not benefit from trying to force it back in.
Some people are telling you that it can never be done or it's just not going to work. It's not their fault for misunderstanding but you don't need to suffer the same way as them.
Let' say you have an ordinary class, Account, with a few async methods -
class Account {
constructor(balance) {
this.balance = balance
}
async withdraw (x) {
await sleep(1000)
this.balance -= x
}
async deposit (x) {
await sleep(1000)
this.balance += x
}
}
sleep is a simple function which delays the program for ms milliseconds -
const sleep = ms =>
new Promise(r => setTimeout(r, ms))
Now we can write a chain function -
const chain = t =>
new Proxy(Promise.resolve(t), { get: get(t) })
const get = t => (target, prop) => (...args) =>
prop === "then"
? target[prop](...args)
: chain(target.then(async v => (await v[prop](...args), v)))
This seemingly allows us to mix synchronous and asynchronous behaviour -
const A = new Account(100)
const B = new Account(200)
chain(A).deposit(5).withdraw(20).then(a => console.log("A", a))
chain(B).withdraw(20).withdraw(30).deposit(10).then(b => console.log("B", b))
Run the snippet below to verify the result in your own browser -
const sleep = ms =>
new Promise(r => setTimeout(r, ms))
const get = t => (target, prop) => (...args) =>
prop === "then"
? target[prop](...args)
: chain(target.then(async v => (await v[prop](...args), v)))
const chain = t =>
new Proxy(Promise.resolve(t), { get: get(t) })
class Account {
constructor(balance) {
this.balance = balance
}
async withdraw (x) {
await sleep(1000)
this.balance -= x
}
async deposit (x) {
await sleep(1000)
this.balance += x
}
}
const A = new Account(100)
const B = new Account(200)
chain(A).deposit(5).withdraw(20).then(a => console.log("A", a))
chain(B).withdraw(20).withdraw(30).deposit(10).then(b => console.log("B", b))
console.log("running...")
A { balance: 85 }
B { balance: 160 }
Invent your own convenience. It's your program to do with as you please.

Execute promises map sequentially

I have written a function that is being called in a loop (map) and that function is using promises. Now, I want that function to run synchronously and exit before its next instance is called.
function t1(){
let arr1 = [1,2,3,4,5];
return Promise.map(arr1, (val) =>{
const params = {
"param1" : val1
};
return t2(params);
});
}
function t2(event){
return Promise.resolve()
.then({
//do something
//code doesn't reach here in sync manner. all five instance are invoked and then code reaches here for first instance and so on
})
.then({
//promise chaining. do something more
})
}
t2 is beingcalled five times, but I want each instance to be called only after the instance before it has returned the value.
Currently its not behaving like that but invoking the function five times in parallel.
I can't use async/await due to project limitations.
The problem with your current code is that Promise.prototype.map, like forEach, does not wait for asynchronous functions called inside it to complete. (No asynchronous call will ever be waited for unless you tell the interpreter to do so explicitly with await or .then)
Have t1 await each call of t2:
async function t1(){
let arr1 = [1,2,3,4,5];
const results = [];
for (const val of arr1) {
results.push(await t2(val));
}
return results;
}
Or if you want to use reduce instead of async/await:
const delay = () => new Promise(res => setTimeout(res, 500));
function t1(){
let arr1 = [1,2,3,4,5];
return arr1.reduce((lastProm, val) => lastProm.then(
(resultArrSoFar) => t2(val)
.then(result => [...resultArrSoFar, result])
), Promise.resolve([]));
}
function t2(event){
return delay().then(() => {
console.log('iter');
return event;
});
}
t1()
.then(results => console.log('end t1', results));
Or, if you need the sequential functionality to be encapsulated in t2, then have t2 have a semi-persistent variable of the previous Promise it generated:
const delay = () => new Promise(res => setTimeout(res, 500));
const t1 = () => {
return Promise.all([1, 2, 3, 4].map(t2));
};
const t2 = (() => {
let lastProm = Promise.resolve();
return (event) => {
const nextProm = lastProm
.catch(() => null) // you may or may not want to catch here
.then(() => {
// do something with event
console.log('processing event');
return delay().then(() => event);
});
lastProm = nextProm;
return nextProm;
};
})();
t1().then(results => console.log('t1 done', results));
(function loop(index) {
const next = promiseArray[index];
if (!next) {
return;
}
next.then((response) => {
// do Something before next
loop(index + 1);
}).catch(e => {
console.error(e);
loop(index + 1);
});
})(0 /* startIndex */)
Here is what running Promises sequentially would look like when using .reduce() in combination with async/await:
async function main() {
const t2 = (v) => Promise.resolve(v*2)
const arr1 = [1,2,3,4,5];
const arr1_mapped = await arr1.reduce(async (allAsync, val) => {
const all = await allAsync
all.push(await t2(val) /* <-- your async transformation */)
return all
}, [])
console.log(arr1_mapped)
}
main()

Filtering an array with a function that returns a promise

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.

Categories