Function calls from data.map are not executed synchronously - javascript

I am trying to call same function from map. Depending upon number of entries function will be called.
Here is my code
getDetails = (inputData) => {
const data = {
accountName: "test",
};
let url = `/rest/jarvis/reports/v1/getData`;
let thisdata = this;
post(url, data)
.then((response) => {
if (
response.data &&
response.data.sb &&
response.data.sb.length > 0
) {
this.props.bs.betDetails = {
data: [...response.data.sportsbookBets],
exportAccesses: response.data.exportAccesses,
};
this.props.bs.betDetails.data.map((value, index) => {
console.log("Index Value --->", index);
this.getEventDetails(value, index);
}),
this.updateReducer({
...this.props.bs,
});
}
}
}
getEventDetails = async (value, index) => {
let _this = this;
let myPromise = new Promise(function (myResolve, myReject) {
console.log("Index Value at the entry--->", index);
const data = {
accountName: "test",
};
let url = `/rest/jarvis/reports/v1/getBetEventData`;
post(url, data).then((response) => {
if (response.data) {
console.log("Index Value inside--->", index);
_this.eventIdIndexMap.set(
list.eventId != null
? list.eventId
: _this.props.bs.betEventRequest.EventId,
index
);
_this.props.bs.testEventDetails[
_this.eventIdIndexMap.get(
list.eventId != null
? list.eventId
: _this.props.bs.betEventRequest.EventId
)
] = _this.props.bs.betEventResponse;
}}}
await myPromise;
}
Here problem I am facing is inside post call of 'getEventDetails' index values are not getting in sequence.
I have used promise await and async but it is not working.
Can someone please tell me how to get it properly ?

map calls its callback function without await. You can use asynchronous functions in the callback function but they won't be executed in order.
But you can create your own map with await:
async function map(array, cb) {
const result = [];
for (const element of array) {
result.push(await cb(element));
}
return result;
}
async function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
(async () => {
console.log(await map([3, 2, 1], async el => { await sleep(el * 100); return 2 * el; }));
})();
or create your own Array.prototype.syncMap:
Array.prototype.syncMap = async function(cb) {
const result = [];
for (const element of this) {
result.push(await cb(element));
}
return result;
}
async function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
(async () => {
console.log(await [3, 2, 1].syncMap(async el => { await sleep(el * 100); return 2 * el; }));
})();
It looks like you don't even need a result. You can write your own Array.prototype.syncForEach:
Array.prototype.syncForEach = async function(cb) {
for (const element of this) {
await cb(element);
}
}
async function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
[3, 2, 1].syncForEach(async el => { await sleep(el * 100); console.log(2 * el); });
[30, 20, 10].forEach(async el => { await sleep(el * 10); console.log(2 * el); });
As you can see in the output the functions in syncForEach are executed in order and the functions in forEach are executed out of order.

Related

How i can call function inside function

I would like to call a function that is inside another function, that function will clear the timeout.
I have tried the following code, but without success:
async function Blast2() {
const delayTime = 1000;
const timer = (ms) => new Promise((res) => setTimeout(res, ms));
function ClearDelayTime() {
return clearTimeout(blast);
}
const blast = setTimeout(function () {
let blast =
"SELECT * FROM admin_contacts,temporary WHERE blast_status = 'sended'";
db.query(blast, async function (err, result, field) {
if (err) throw err;
loop: {
for (var i = 0; i < result.length; i++) {
console.log(result[i].telefone);
await timer(delayTime); // then the created Promise can be awaited
}
}
});
}, delayTime);
}
// I Want Call the function ClearDelayTime() inside Blast2()
Blast2().ClearDelayTime();
I've refactored your code. It now immediately returns a function that can be used to abort.
const db = {
query: (sql, callback) => callback(undefined,
[{telefone:1},{telefone:2},{telefone:3},{telefone:4},{telefone:5}])
}
function blast2() {
const delayTime = 1000
const timer = ms => new Promise(res => setTimeout(res, ms))
let sql = "SELECT * FROM admin_contacts,temporary WHERE blast_status = 'sended'";
let abort = false;
db.query(sql, (err, result) => {
if (!err) {
(async () => {
for(let i=0; i<result.length && !abort; i++) {
console.log(result[i].telefone);
await timer(delayTime);
}
})();
}
})
return () => abort = true;
}
let abortFunc = blast2();
setTimeout(abortFunc, 2500);
Your function Blast2 doesn't return anything. If you want to call a function inside it, you could return and store it using a variable, then call it.
Something like this:
const f1 = () => {
console.log('f1 called')
const f2 = () => {
console.log('f2 called')
}
return f2
}
const returnedFunction = f1()
console.log(returnedFunction())

How to write await() for multiple promises in JavaScript?

I m trying to write await for multiple promises, instead of nested functions.
Please take a look at the below code i tried, as it will explain better than me.
var main = async () => {
// const main_ = await Promise.all(fun1,fun2,fun3);
// Fun 3
const fun3 = () =>
new Promise((resolve) => async () => {
// console.log(1);
return resolve(await fun2(1));
});
// Fun 2
const fun2 = (value) =>
new Promise((resolve) => async (value) => {
value = value + 1;
// console.log(value);
return resolve(await fun1(value));
});
// Fun 1
const fun1 = () =>
new Promise((resolve) => (value) => {
value = value + 1;
// console.log(value);
return resolve(value);
});
fun3();
};
main();
I tried console logging to debut but I m getting nothing in the console.
Any help is greatly appreciated.
the syntax is wrong, its not new Promise((resolve) => async () => {}) with 2 arrow, but new Promise((resolve) => {}) also you can call promise function without await
var main = async () => {
// const main_ = await Promise.all(fun1,fun2,fun3);
// Fun 3
const fun3 = () => new Promise(resolve => {
//console.log(1);
return resolve(fun2(1));
});
// Fun 2
const fun2 = (value) => new Promise(resolve => {
value = value + 1;
//console.log(value);
return resolve(fun1(value));
});
// Fun 1
const fun1 = (value) => new Promise(async (resolve) => {
value = value + 1;
console.log('sleep 3 seconds');
await new Promise(r => setTimeout(r, 3000));
console.log(value);
return resolve(value);
});
fun3();
};
main();
If you want to call await inside a Promise callback, you can do this:
const p = new Promise((resolve) => {
(async () => {
const res = await anotherPromise();
resolve(res);
})();
});
So with this in mind, you can rewrite your functions like this:
var main = async () => {
// const main_ = await Promise.all(fun1,fun2,fun3);
// Fun 3
const fun3 = () =>
new Promise((resolve) => {
(async () => {
resolve(await fun2(1));
})();
});
// Fun 2
const fun2 = (value) =>
new Promise((resolve) => {
value = value + 1;
(async () => {
resolve(await fun1(value));
})();
});
// Fun 1
const fun1 = (value) =>
new Promise((resolve) => {
value = value + 1;
(async () => {
resolve(value);
})();
});
return await fun3();
};
// ans: 3
console.log(await main());
If you want to do it in pure async/await, you may do this:
const main = async () => {
const fun1 = async (value) => value + 1;
const fun2 = async (value) => await fun1(value + 1);
const fun3 = async () => await fun2(1);
return await fun3();
}
// output: 3
console.log(await main());
I hope this example works for you.
console.clear();
function wait(ms, data) {
return new Promise( resolve => setTimeout(resolve.bind(this, data), ms) );
}
/**
* These will be run in series, because we call
* a function and immediately wait for each result,
* so this will finish in 1s.
*/
async function series() {
return {
result1: await wait(500, 'seriesTask1'),
result2: await wait(500, 'seriesTask2'),
}
}
/**
* While here we call the functions first,
* then wait for the result later, so
* this will finish in 500ms.
*/
async function parallel() {
const task1 = wait(500, 'parallelTask1');
const task2 = wait(500, 'parallelTask2');
return {
result1: await task1,
result2: await task2,
}
}
async function taskRunner(fn, label) {
const startTime = performance.now();
console.log(`Task ${label} starting...`);
let result = await fn();
console.log(`Task ${label} finished in ${ Number.parseInt(performance.now() - startTime) } miliseconds with,`, result);
}
void taskRunner(series, 'series');
void taskRunner(parallel, 'parallel');
fun3() is an async function so you have to put await before the call.
var main = async () => {
// Fun 3
const fun3 = () => new Promise(resolve => {
//console.log(1);
return resolve(fun2(1));
});
// Fun 2
const fun2 = (value) => new Promise(resolve => {
value = value + 1;
//console.log(value);
return resolve(fun1(value));
});
// Fun 1
const fun1 = (value) => new Promise(async (resolve) => {
value = value + 1;
console.log('sleep 3 seconds');
await new Promise(r => setTimeout(r, 3000));
console.log(value);
return resolve(value);
});
await fun3();
};
main();

Not able to set item with the key in the final object

The value of videoProgress is not returned in the final object document, i want videoProgress to be returned along with the documentList item, but i am not getting it, i have set item.videoProgress too but not getting it in response:
const document = await Promise.all(documentList.map(async (item) => {
const videoCount = await studentProgressModel.find({ gradeId: item.gradeId, userId : item._id, type: 'VIDEO'}).countDocuments();
const totalVideoCount = await videoModel.find({ gradeId: item.gradeId, mediumId: item.mediumId, status: 'ACTIVE' }).countDocuments();
let videoPercentage = 0;
if(totalVideoCount > 0) {
videoPercentage = (100 * videoCount) / totalVideoCount;
}
item.videoProgress = videoPercentage;
console.log('item', item);
return item;
}));
The issue here is not what anyone in the comments above is discussing, the issue is that you are not actually returning a promise from your documentList.map, but instead are just writing an async function there. Array.map will not wait for any asynchronous behavior, so as soon as it hits an await it returns an empty result.
This can be fixed by instead returning promises in your map:
const document = await Promise.all(documentList.map((item) => { // Removed async here, map is synchronous
// Here we *immediately* return a promise so that map can return that to the promise.all
return new Promise(async (resolve) => {
// Now we can do all our async stuff and promise.all will wait for it :)
const videoCount = await studentProgressModel.find({ gradeId: item.gradeId, userId : item._id, type: 'VIDEO'}).countDocuments();
const totalVideoCount = await videoModel.find({ gradeId: item.gradeId, mediumId: item.mediumId, status: 'ACTIVE' }).countDocuments();
let videoPercentage = 0;
if(totalVideoCount > 0) {
videoPercentage = (100 * videoCount) / totalVideoCount;
}
item.videoProgress = videoPercentage;
console.log('item', item);
return resolve(item); // have to use resolve here with our result to resolve the promise.
});
}));
There are other ways to do this that don't involve an explicit new-promise wrapper, but I figured it made the promise behavior more obvious
Edit: Here's an example which can be run directly here in your browser, I have only replaced the queries with async random number generators.
function randomInteger() {
return new Promise((resolve) => {
setTimeout(() => {
return resolve(Math.floor(Math.random() * 100));
}, 1);
});
}
async function main() {
let documentList = [{}, {}, {}];
const document = await Promise.all(documentList.map((item) => {
return new Promise(async (resolve) => {
const videoCount = await randomInteger();
const totalVideoCount = await randomInteger();
let videoPercentage = 0;
if(totalVideoCount > 0) {
videoPercentage = (100 * videoCount) / totalVideoCount;
}
item.videoProgress = videoPercentage;
console.log('item', item);
return resolve(item);
});
}));
console.log('Document results:', document);
}
main();

Return dependent promises sequentially inside loop

I'm working on shopify integration.
We receive an array items then loop through them and add them a new model (func1) then I need to use that result from the first and add it to a schedule (func2).
I need this functions to run sequentially because I'm adding the results to a schedule and if I have two results for the same date and they don't yet exist in the database if the they run in parallel it creates 2 separate entries in the database instead of one entry with the two values.
The way I need to return is func1, func2, func1, func2....
But at the moment is returning func1, func1...func2, func2...
This is a simplified example of what I need to accomplish.
const func2 = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
return console.log('func2');
}, 3000);
});
};
const func1 = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('Func1');
func2();
}, 1000);
});
};
const array = [1, 2, 3, 4, 5];
const test = () => {
array.map(x => {
func1();
});
};
test();
If there is something that isn't clear please let me know.
Thanks
you can use async/await and for loop in order do create a synced like iteration. and use it again in your func1 in order to reslove
const func2 = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('func2');
resolve();
}, 3000);
});
};
const func1 = () => {
return new Promise( (resolve, reject) => {
setTimeout(async () => {
console.log('Func1');
await func2();
resolve();
}, 1000);
});
};
const array = [1, 2, 3, 4, 5];
const test = async () => {
for(let i=0;i<array.length;i++){
await func1();
}
};
test();
This is the perfect place to use the traverse function:
const traverse = (xs, f) => xs.reduce((promise, x) =>
promise.then(ys => f(x).then(y => Promise.resolve(ys.concat([y])))),
Promise.resolve([]));
const times2 = x => new Promise(resolve => setTimeout(() => {
console.log("times2", x);
resolve(2 * x);
}, 1000));
const minus1 = x => new Promise(resolve => setTimeout(() => {
console.log("minus1", x);
resolve(x - 1);
}, 1000));
const xs = [1,2,3,4,5];
const ys = traverse(xs, x => times2(x).then(minus1));
ys.then(console.log); // [1,3,5,7,9]
Hope that helps.
const func2 = async (modalValue) => {
let result = modalValue*5;
console.log(`Function2 result: ${result}`)
return result;
};
async function getModalValue(i){
// rest of your Code
return { modal: i*2}
}
const func1 = async (item) => {
let {modal} = await getModalValue(item);
console.log(`Function1 modal: ${modal}`)
await func2(modal);
};
const array = [1, 2, 3, 4, 5];
const test = async () => {
for(let i=0;i<array.length;i++){
await func1(array[i]);
}
};
test().then((resp)=> {console.log("Completed")})
.catch((err)=>{console.log("failure")})

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