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.
As a side note, we can return a promise from any function. It doesn't have to be asynchronous. That being said, promises are normally returned in cases where the function they return from is asynchronous. For example, an API that has methods for saving data to a server would be a great candidate to return a promise!
filter() method is having an asynchronous behavior.
The Promise. all() method takes an iterable of promises as an input, and returns a single Promise that resolves to an array of the results of the input promises. This returned promise will fulfill when all of the input's promises have fulfilled, or if the input iterable contains no promises.
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With