Member-only story

Asynchronous programming in JavaScript has evolved from callbacks to Promises and has now widely adopted the async/await syntax. The latter not only makes asynchronous code more concise but also aligns it closely with the logic and structure of synchronous code, greatly enhancing code readability and maintainability. After mastering the basics, let’s explore some advanced usages of async/await to efficiently handle more complex asynchronous flow control.
- async/await with Higher-Order Functions
When performing asynchronous operations on elements in an array, combine async/await with higher-order functions like map and filter.
// Asynchronous filtering function
async function asyncFilter(array, predicate) {
const results = await Promise.all(array.map(predicate));
return array.filter((_value, index) => results[index]);
}
// Example
async function isOddNumber(n) {
await delay(100); // Simulate asynchronous operation
return n % 2 !== 0;
}
async function filterOddNumbers(numbers) {
return asyncFilter(numbers, isOddNumber);
}
filterOddNumbers([1, 2, 3, 4, 5]).then(console.log); // Output: [1, 3, 5]
2. Controlling Concurrency
In scenarios like file uploads, limiting the number of simultaneous asynchronous operations can prevent resource exhaustion.
async function asyncPool(poolLimit, array, iteratorFn) {
const result = [];
const executing = [];
for (const item of array) {
const p = Promise.resolve().then(() => iteratorFn(item, array));
result.push(p);
if (poolLimit <= array.length) {
const e = p.then(() => executing.splice(executing.indexOf(e), 1));
executing.push(e);
if (executing.length >= poolLimit) {
await Promise.race(executing);
}
}
}
return Promise.all(result);
}
// Example
async function uploadFile(file) {
// File upload logic
}
async function limitedFileUpload(files) {
return asyncPool(3, files, uploadFile);
}
3. Optimizing Recursion with async/await
Asynchronous operations in recursive functions become straightforward with async/await.
// Asynchronous recursive function
async function…