You can find all the code in this post at the repo Github.
Async programming Promise related challenges
Add two promises
/**
* @param {Promise} promise1
* @param {Promise} promise2
* @return {Promise}
*/
async function addTwoPromises(promise1, promise2) {
return new Promise(async (resolve) => {
const result1 = await promise1;
const result2 = await promise2;
return resolve(result1 + result2);
});
}
// Usage example
addTwoPromises(
Promise.resolve(2), Promise.resolve(2)
)
.then(console.log); // => 4
Async addition
function asyncAdd(a, b, callbackFn) {
setTimeout(() => {
callbackFn(null, a + b);
}, Math.random() * 1000);
}
async function sum(...args) {
let total = 0;
// helper
const add = (a, b) => {
return new Promise((resolve) => {
asyncAdd(a, b, (err, result) => {
if (err) {
throw err;
}
resolve(result);
});
});
};
for (const arg of args) {
total = await add(total, arg);
}
return total;
}
// Usage example
async function total() {
const res1 = await sum(1, 2, 3, 4, 5, 6, 4);
const res2 = await sum(1, 2, 3, 4, 5, 6, 4);
return [res1, res2];
}
total().then((result) => console.log(result));
Auto retry promise on rejection
/**
* @param {() => Promise<any>} fetcher
* @param {number} maximumRetryCount
* @return {Promise<any>}
*/
// Recursive approach
function fetchWithAutoRetry(fetcher, maximumRetryCount = 5) {
return fetcher()
.catch((err) => {
if (maximumRetryCount === 0) {
throw err;
} else {
return fetchWithAutoRetry(fetcher, maximumRetryCount - 1);
}
});
}
// Iterative approach
function fetchWithAutoRetry(fetcher, maximumRetryCount = 5) {
return new Promise((resolve, reject) => {
function attempt(count) {
fetcher()
.then(resolve)
.catch((err) => {
if (count === 0) {
reject(err);
} else {
attempt(count - 1);
}
});
}
attempt(maximumRetryCount);
});
}
// Usage example
function simulateAPICall() {
return new Promise((resolve, reject) => {
// Simulate a 50% chance of failure
if (Math.random() < 0.5) {
reject(new Error('API call failed'));
} else {
resolve('API call succeeded');
}
});
}
fetchWithAutoRetry(simulateAPICall, 3)
.then(result => {
console.log('Success:', result);
})
.catch(error => {
console.error('All retries failed:', error.message);
});
// Example output (may vary due to randomness):
// Success: API call succeeded
// Or if all retries fail:
// All retries failed: API call failed
Call APIs with pagination
// const fetchList = (since?: number) =>
// Promise<{items: Array<{id: number}>}>
const fetchListWithAmount = async (amount = 5) => {
const result = [];
function request(id) {
return fetchList(id).then(({ items }) => {
result.push(...items);
if (result.length >= amount || !items.length) {
return result.slice(0, amount);
}
const { id: lastItemId } = result.at(-1);
return request(lastItemId);
});
}
return request();
}
Cancel request
const URL = 'https://jsonplaceholder.typicode.com/posts/1';
const TIMEOUT = 5000;
function fetchDataWithTimeout(url, timeout) {
const controller = new AbortController();
const { signal } = controller;
const timerId = setTimeout(() => {
controller.abort();
}, timeout);
return fetch(url, { signal })
.then((response) => {
clearTimeout(timerId);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
})
.catch((err) => {
if (err.name === 'AbortError') {
throw new Error('Fetch operation timed out');
} else {
throw err;
}
});
}
// Usage example
fetchDataWithTimeout(URL, TIMEOUT)
.then(data => {
console.log('Fetched data:', data);
console.log('Title:', data.title);
console.log('Body:', data.body);
})
.catch(error => {
console.error('Error:', error.message);
});
/*
Fetched data: {
userId: 1,
id: 1,
title: 'sunt aut facere repellat provident occaecati excepturi optio reprehenderit',
body: 'quia et suscipit\n' +
'suscipit recusandae consequuntur expedita et cum\n' +
'reprehenderit molestiae ut ut quas totam\n' +
'nostrum rerum est autem sunt rem eveniet architecto'
}
Title: sunt aut facere repellat provident occaecati excepturi optio reprehenderit
Body: quia et suscipit
suscipit recusandae consequuntur expedita et cum
reprehenderit molestiae ut ut quas totam
nostrum rerum est autem sunt rem eveniet architecto
*/
// Or
// Error: Request timed out || Error: [Specific error message]
Concurrent data fetching
const URL_ARR = [
'https://fastly.picsum.photos/id/0/5000/3333.jpg?hmac=_j6ghY5fCfSD6tvtcV74zXivkJSPIfR9B8w34XeQmvU',
'https://fastly.picsum.photos/id/1/5000/3333.jpg?hmac=Asv2DU3rA_5D1xSe22xZK47WEAN0wjWeFOhzd13ujW4',
'https://fastly.picsum.photos/id/2/5000/3333.jpg?hmac=_KDkqQVttXw_nM-RyJfLImIbafFrqLsuGO5YuHqD-qQ',
'https://fastly.picsum.photos/id/3/5000/3333.jpg?hmac=GDjZ2uNWE3V59PkdDaOzTOuV3tPWWxJSf4fNcxu4S2g'
];
function fetchData(url) {
return fetch(url)
.then((response) => {
if (!response.ok) {
throw new Error(`HTTP error: ${response.statu}`);
}
return response.blob();
});
}
Promise.all(URL_ARR.map(fetchData))
.then((results) => {
console.log(results);
})
.catch((err) => {
console.log(err);
});
/*
[
Blob { size: 490823, type: 'image/jpeg' },
Blob { size: 417790, type: 'image/jpeg' },
Blob { size: 451977, type: 'image/jpeg' },
Blob { size: 395440, type: 'image/jpeg' }
]
*/
LazyMan
class LazyMan {
constructor(name) {
this.name = name;
console.log(`My name is ${name}`);
this.taskQueue = [];
setTimeout(() => {
return this.next();
}, 0);
}
eat(food) {
this.taskQueue.push(() => {
console.log(`I am eating ${food}`);
this.next();
});
return this;
}
sleep(delay) {
this.taskQueue.push(() => {
console.log('I am sleeping...');
setTimeout(() => {
console.log(`After ${delay} seconds`);
this.next();
}, delay);
});
return this;
}
next() {
const fn = this.taskQueue.shift();
if (typeof fn === 'function') {
fn();
}
}
}
// Usage example
const lazyMan = new LazyMan('jack');
lazyMan.eat('apple').sleep(5000).eat('hamburger').sleep(3000).eat('pear');
/*
My name is jack
I am eating apple
I am sleeping...
After 5000 seconds
I am eating hamburger
I am sleeping...
After 3000 seconds
I am eating pear
*/
Load images concurrently
function loadImage(url) {
return new Promise((resolve, reject) => {
const img = new Image();
img.onload = () => resolve(img);
img.onerror = () => reject(new Error(`Could not load image at ${url}`));
img.src = url;
});
}
function loadImages(urls) {
const promises = urls.map(url => loadImage(url));
return Promise.all(promises)
.then(images => {
images.forEach(img => document.body.appendChild(img));
console.log('All images loaded successfully!');
})
.catch(err => {
console.error(err);
});
}
// Usage example
const imageUrls = [
'https://picsum.photos/200/300',
'https://picsum.photos/200/300?random=1',
'https://picsum.photos/200/300?random=2',
'https://picsum.photos/200/300?random=3',
'https://picsum.photos/200/300?random=4'
];
loadImages(imageUrls);
Log promise
function sleep(delay) {
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, delay);
});
};
// promise based
function logNumbers() {
let promise = Promise.resolve();
for (let i = 1; i <= 5; i += 1) {
promise = promise.then(() => {
console.log(i);
return sleep(1000);
});
}
return promise;
}
// async-await based
async function logNumbers() {
for (let i = 1; i <= 5; i += 1) {
console.log(i);
await sleep(1000);
}
}
// Usage example
logNumbers()
.then(() => {
console.log('finished!'); // => 'finished!'
});
Make a request
const URL = 'https://randomuser.me/api/';
/**
* @param {string} url
* @return {promise}
*/
// promise-based
function fetchData(url) {
return fetch(url)
.then((response) => {
if (!response.ok) {
throw new Error(`Error: ${response.status}`);
}
return response.json();
})
.catch((err) => {
console.error('Fetch error:', err);
throw err;
});
}
// async-await based
async function fetchData(url) {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`Error: ${response.status} - ${response.statusText}`);
}
return await response.json();
} catch (err) {
console.error(`Request error: ${err}`);
throw err;
}
}
fetchData(URL)
.then((data) => {
console.log(data);
})
.catch((err) => {
console.log(`Request error: ${err}`);
});
/*
{
results: [
{
gender: 'male',
name: [Object],
location: [Object],
email: 'joanikije.lazic@example.com',
login: [Object],
dob: [Object],
registered: [Object],
phone: '034-5670-262',
cell: '067-5723-987',
id: [Object],
picture: [Object],
nat: 'RS'
}
],
info: { seed: '38e4419ddfa3cd4d', results: 1, page: 1, version: '1.4' }
}
*/
Map async
/**
* @params {Array} iterable
* @params {callbackFn} Function
* @return {Array}
*/
// Use Promise.all()
function mapAsync(iterable, callbackFn) {
return Promise.all(iterable.map(callbackFn));
}
// Iterative
function mapAsync(iterable, callbackFn) {
return new Promise((resolve, reject) => {
const results = [];
let unresolved = iterable.length;
if (!unresolved) {
resolve(results);
return;
}
iterable.forEach((item, index) => {
callbackFn(item)
.then((result) => {
results[index] = result;
unresolved -= 1;
if (!unresolved) {
resolve(results);
}
})
.catch((err) => {
reject(err);
});
});
});
}
// Usage example
function asyncDouble(x) {
return new Promise((resolve) => {
setTimeout(() => {
resolve(x * 2);
}, 1000);
});
}
mapAsync([1, 2], asyncDouble)
.then((results) => {
console.log(results); // => [2, 4]
});
Map async limit
/**
* @param {Array<any>} iterable
* @param {Function} callbackFn
* @param {number} size
* @return {Promise}
*/
// Chunk approach
async function mapAsyncLimit(iterable, callbackFn, size = Infinity) {
const results = [];
for (let i = 0; i < iterable.length; i += size) {
const chunk = iterable.slice(i, i + size);
const chunkResults = await Promise.all(chunk.map(callbackFn));
results.push(...chunkResults);
}
return results;
}
// Chunkless approach
function mapAsyncLimit(iterable, callbackFn, size = Infinity) {
return new Promise((resolve, reject) => {
const results = [];
let resolved = 0;
let nextIndex = 0;
let len = iterable.length;
if (!len) {
resolve(results);
return;
}
async function processItem(index) {
nextIndex += 1;
try {
const result = await callbackFn(iterable[index]);
results[index] = result;
resolved += 1;
if (resolved === len) {
resolve(results);
return;
}
if (nextIndex < len) {
processItem(nextIndex);
}
} catch (err) {
reject(err);
}
}
for (let i = 0; i < Math.min(len, size); i += 1) {
processItem(i);
}
});
}
// Promise-based
function mapAsyncLimit(iterable, callbackFn, size = Infinity) {
return new Promise((resolve, reject) => {
const results = [];
let resolved = 0;
let nextIndex = 0;
let len = iterable.length;
if (!len) {
resolve(results);
return;
}
function processItem(index) {
nextIndex += 1;
callbackFn(iterable[index])
.then((result) => {
results[index] = result;
resolved += 1;
if (resolved === len) {
resolve(results);
return;
}
if (nextIndex < len) {
processItem(nextIndex);
}
}).catch(reject);
}
for (let i = 0; i < Math.min(len, size); i += 1) {
processItem(i);
}
});
}
// Usage example
function asyncSquare(x) {
return new Promise((resolve) => {
setTimeout(() => {
console.log(`Processing ${x}`);
resolve(x * x);
}, Math.random() * 1000);
});
}
// Usage example
async function runExample() {
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
const chunkSize = 3;
console.log("Starting async processing...");
const results = await mapAsyncLimit(numbers, asyncSquare, chunkSize);
console.log("All processing complete.");
console.log("Results:", results);
}
runExample();
/*
Starting async processing...
Processing 1
Processing 2
Processing 3
Processing 4
Processing 5
Processing 6
Processing 7
Processing 8
Processing 9
Processing 10
All processing complete.
Results: [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
*/
parallel
function promisify(callbackFn) {
return function (...args) {
return new Promise((resolve, reject) => {
callbackFn((err, data) => {
if (err) {
reject(err);
return;
} else {
resolve(data);
}
}, ...args);
});
}
}
/**
* @param {AsyncFunc[]} fns
* @return {Function}
*/
function parallel(fns) {
return function (callbackFn, ...args) {
return Promise.all(fns.map((fn) => promisify(fn)(...args)))
.then((data) => callbackFn(undefined, data))
.catch((err) => callbackFn(err, undefined));
}
}
// Usage example
function asyncFunc1(callback, input) {
setTimeout(() => {
callback(null, input * 2);
}, 1000);
}
function asyncFunc2(callback, input) {
setTimeout(() => {
callback(null, input + 10);
}, 2000);
}
function asyncFunc3(callback, input) {
setTimeout(() => {
callback(null, input * input);
}, 3000);
}
const asyncFunctions = [asyncFunc1, asyncFunc2, asyncFunc3];
const parallelExecution = parallel(asyncFunctions);
parallelExecution((err, results) => {
if (err) {
console.error('Error:', err);
} else {
console.log('Results:', results);
}
}, 5); // => Results: [ 10, 15, 25 ]
Parallel downloads
const URL_ARR = [
'https://fastly.picsum.photos/id/0/5000/3333.jpg?hmac=_j6ghY5fCfSD6tvtcV74zXivkJSPIfR9B8w34XeQmvU',
'https://fastly.picsum.photos/id/1/5000/3333.jpg?hmac=Asv2DU3rA_5D1xSe22xZK47WEAN0wjWeFOhzd13ujW4',
'https://fastly.picsum.photos/id/2/5000/3333.jpg?hmac=_KDkqQVttXw_nM-RyJfLImIbafFrqLsuGO5YuHqD-qQ',
'https://fastly.picsum.photos/id/3/5000/3333.jpg?hmac=GDjZ2uNWE3V59PkdDaOzTOuV3tPWWxJSf4fNcxu4S2g'
];
function fetchData(url) {
return fetch(url)
.then((response) => {
if (!response.ok) {
throw new Error(`Error: ${response.status}`);
}
return response.blob();
})
.catch((err) => {
throw new Error('Error');
});
}
Promise.all(URL_ARR.map(fetchData))
.then((data) => {
console.log(data);
})
.catch((err) => {
console.log(err);
});
/*
[
Blob { size: 490823, type: 'image/jpeg' },
Blob { size: 417790, type: 'image/jpeg' },
Blob { size: 451977, type: 'image/jpeg' },
Blob { size: 395440, type: 'image/jpeg' }
]
*/
Ping and Calc
function ping(delay) {
return new Promise((resolve) => {
setTimeout(() => {
resolve('Ping successful');
}, delay);
});
}
function calc(interval) {
let start = Date.now();
let count = 0;
let timerId = null;
function loop() {
const drift = Date.now() - start - count * interval;
count += 1;
timerId = setTimeout(async () => {
const result = await ping(1000);
console.log(result);
const elapsedTime = Math.floor((Date.now() - start) / 1000);
console.log(`Elapsed time: ${elapsedTime} seconds`);
loop();
}, interval - drift);
}
loop();
return {
clear: () => {
clearTimeout(timerId);
console.log('Ping calculation aborted.');
}
};
}
// Example usage:
const pingCalculator = calc(5000);
setTimeout(() => {
pingCalculator.clear();
}, 20000); // Stops after 20 seconds
/*
Ping successful
Elapsed time: 6 seconds
Ping successful
Elapsed time: 11 seconds
Ping successful
Elapsed time: 16 seconds
Ping calculation aborted.
*/
Promise merge
function isPlainObject(value) {
// For null and undefined
if (value == null) {
return false;
}
const prototype = Object.getPrototypeOf(value);
return prototype === Object.prototype || prototype === null;
}
function mergeResult(result1, result2) {
try {
if (typeof result1 === 'number' && typeof result2 === 'number') {
return result1 + result2;
}
if (typeof result1 === 'string' && typeof result2 === 'string') {
return result1 + result2;
}
if (Array.isArray(result1) && Array.isArray(result2)) {
return [...result1, ...result2];
}
if (isPlainObject(result1) && isPlainObject(result2)) {
return { ...result1, ...result2 };
}
throw 'Unsupported data types';
} catch {
throw 'Unsupported data types';
}
}
/**
* @param {Promise} p1
* @param {Promise} p2
* @return {Promise<any>}
*/
function promiseMerge(p1, p2) {
let unresolved = 2;
let p1Result;
let p2Result;
return new Promise((resolve, reject) => {
function then() {
unresolved -= 1;
if (!unresolved) {
resolve(mergeResult(p1Result, p2Result));
}
}
p1.then((result) => {
p1Result = result;
then();
}).catch(reject);
p2.then((result) => {
p2Result = result;
then();
}).catch(reject);
});
}
// Usage example
const p1 = Promise.resolve(10);
const p2 = Promise.resolve(20);
promiseMerge(p1, p2)
.then((result) => {
console.log(result); // => 30
});
Promise scheduler
class Scheduler {
constructor(concurrencyLimit) {
this.taskQueue = [];
this.concurrencyLimit = concurrencyLimit;
this.runningTasks = 0;
}
addTask(delay, taskId) {
const task = () => {
return new Promise((resolve) => {
setTimeout(() => {
console.log(`Executing task: ${taskId}`);
resolve();
}, delay);
});
}
this.taskQueue.push(task);
}
async start() {
console.log(`Starting scheduler with concurrency limit: ${this.concurrencyLimit}`);
for (let i = 0; i < this.concurrencyLimit; i += 1) {
this.executeNextTask();
}
}
async executeNextTask() {
if (
this.taskQueue.length === 0 ||
this.runningTasks >= this.concurrencyLimit
) {
return;
}
this.runningTasks += 1;
const task = this.taskQueue.shift();
try {
await task();
} catch (err) {
console.error(`Task error: ${err.message}`);
} finally {
this.runningTasks -= 1;
this.executeNextTask();
}
}
get pendingTasksCount() {
return this.taskQueue.length;
}
get activeTasksCount() {
return this.runningTasks;
}
}
// Usage example
const scheduler = new Scheduler(2);
scheduler.addTask(1000, "Task 1");
scheduler.addTask(500, "Task 2");
scheduler.addTask(300, "Task 3");
scheduler.addTask(400, "Task 4");
scheduler.start();
setInterval(() => {
console.log(`Active tasks: ${scheduler.activeTasksCount}, Pending tasks: ${scheduler.pendingTasksCount}`);
}, 500);
/*
Starting scheduler with concurrency limit: 2
Executing task: Task 2
Active tasks: 2, Pending tasks: 1
Executing task: Task 3
Executing task: Task 1
Active tasks: 1, Pending tasks: 0
Executing task: Task 4
Active tasks: 0, Pending tasks: 0
Active tasks: 0, Pending tasks: 0
Active tasks: 0, Pending tasks: 0
*/
Promise time limit
/**
* @param {Function} fn
* @param {number} t
* @return {Function}
*/
function timeLimit (fn, t) {
return function(...args) {
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => {
reject('Time Limit Exceeded');
}, t);
});
const fnPromise = fn(...args);
return Promise.race([timeoutPromise, fnPromise]);
}
};
// Usage example
const limited = timeLimit((t) => new Promise(res => setTimeout(res, t)), 1000);
limited(1500).catch(console.log); // => "Time Limit Exceeded" at t=1000ms
Promisify
/**
* @callback fn
* @returns Function
*/
function promisify(fn) {
return function (...args) {
return new Promise((resolve, reject) => {
fn.call(this, ...args, (err, result) => {
if (err) {
reject(err);
} else {
resolve(result);
}
});
});
}
}
// Usage example
function foo(url, options, callback) {
apiCall(url, options)
.then((data) => callback(null, data))
.catch((err) => callback(err));
}
const promisifiedFoo = promisify(foo);
const data = await promisifiedFoo('example.com', { foo: 1 });
/**
* @callback fn
* @returns Function
*/
const promisifyCustomSymbol = Symbol.for('util.promisify.custom');
function promisify(fn) {
if (fn[promisifyCustomSymbol]) {
return fn[promisifyCustomSymbol];
}
return function (...args) {
return new Promise((resolve, reject) => {
fn.call(this, ...args, (err, result) => {
if (result) {
resolve(result);
} else {
reject(err);
}
});
});
}
}
// Usage example
function foo(url, options, callback) {
apiCall(url, options)
.then((data) => callback(null, data))
.catch((err) => callback(err));
}
const promisifiedFoo = promisify(foo);
const data = await promisifiedFoo('example.com', { foo: 1 });
race
function promisify(callbackFn) {
return function (...args) {
return new Promise((resolve, reject) => {
callbackFn((err, data) => {
if (err) {
reject(err);
return;
} else {
resolve(data);
}
}, ...args);
});
}
}
/**
* @param {AsyncFunc[]} fns
* @return {Function}
*/
function race(fns) {
return function (callbackFn, ...args) {
return Promise.race(fns.map((fn) => promisify(fn)(...args)))
.then((data) => callbackFn(undefined, data))
.catch((err) => callbackFn(err, undefined));
}
}
// Usage example
function asyncFunc1(callback, input) {
setTimeout(() => {
callback(null, input * 2);
}, 1000);
}
function asyncFunc2(callback, input) {
setTimeout(() => {
callback(null, input + 10);
}, 2000);
}
function asyncFunc3(callback, input) {
setTimeout(() => {
callback(null, input * input);
}, 3000);
}
const asyncFunctions = [asyncFunc1, asyncFunc2, asyncFunc3];
const raceExecution = race(asyncFunctions);
raceExecution((err, results) => {
if (err) {
console.error('Error:', err);
} else {
console.log('Results:', results);
}
}, 5); // => Results: 10
Retry
const URL = 'https://jsonplaceholder.typicode.com/posts';
function fetchWithRetry(url, maxRetries) {
return new Promise((resolve, reject) => {
let retries = 0;
function fetchData(url) {
fetch(url)
.then((response) => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
})
.then((data) => {
resolve(data);
})
.catch((err) => {
retries += 1;
if (retries >= maxRetries) {
reject(new Error(`Max retries reached. Last error: ${err.message}`));
} else {
console.log(`Request failed, retrying... (${retries}/${maxRetries})`);
fetchData(url);
}
});
}
fetchData(url);
});
}
fetchWithRetry(URL, 5)
.then((data) => {
console.log(data);
})
.catch((err) => {
console.log(err);
});
Sequence
function promisify(callbackFn) {
return function (...args) {
return new Promise((resolve, reject) => {
callbackFn((err, data) => {
if (err) {
reject(err);
return;
} else {
resolve(data);
}
}, ...args);
});
}
}
/**
* @param {AsyncFunc[]} fns
* @return {Function}
*/
function sequence(fns) {
const promises = fns.map(promisify);
return async function (callbackFn, ...args) {
try {
let result = args;
for (const promise of promises) {
result = await promise(...result);
result = [result];
}
callbackFn(undefined, result[0]);
} catch (err) {
callbackFn(err, undefined);
}
}
}
// Usage example
function asyncFunc1(callback, x, y) {
setTimeout(() => {
callback(null, x + y);
}, 1000);
}
function asyncFunc2(callback, sum) {
setTimeout(() => {
callback(null, sum * 2);
}, 1000);
}
function asyncFunc3(callback, result) {
setTimeout(() => {
callback(null, result + 10);
}, 1000);
}
const asyncFunctions = [asyncFunc1, asyncFunc2, asyncFunc3];
const sequentialExecution = sequence(asyncFunctions);
console.log("Starting sequential execution...");
sequentialExecution((err, result) => {
if (err) {
console.error('Error:', err);
} else {
console.log('Final Result:', result);
}
}, 5, 3);
/*
=> Starting sequential execution...
=> Final Result: 26 (after about 3 seconds)
*/
Sequential async
function asyncOp1(delay) {
return new Promise((resolve) => {
setTimeout(() => {
console.log('async1');
resolve();
}, delay);
});
}
function asyncOp2(delay) {
return new Promise((resolve) => {
setTimeout(() => {
console.log('async2');
resolve();
}, delay);
});
}
async function sequence() {
try {
await asyncOp1(2000);
await asyncOp2(2000);
console.log('finish');
} catch (err) {
console.log(err);
}
}
sequence();
Sleep
/**
* @param {number} duration
* @return {Promise<void>}
*/
async function sleep(duration) {
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, duration);
});
}
// Usage example
async function greeting() {
console.log('Hello!');
await sleep(2000);
console.log('Bye.'); // Only logs after 2000 milliseconds (2 seconds)
}
greeting();
// t = 0: Hello!
// t = 2000: Bye.
Throttle promises
/**
* @param {() => Promise<any>} fns
* @param {number} max
* @return {Promise}
*/
function throttlePromises(fns, max) {
const results = [];
async function doWork(iterator) {
for (let [index, element] of iterator) {
const result = await element();
results[index] = result;
}
}
const iterator = Array.from(fns).entries();
const workers = Array(max).fill(iterator).map(doWork);
return Promise.all(workers).then(() => results);
}
// Usage example
const asyncFunctions = [
() => new Promise(resolve => setTimeout(() => resolve('Task 1'), 1000)),
() => new Promise(resolve => setTimeout(() => resolve('Task 2'), 1500)),
() => new Promise(resolve => setTimeout(() => resolve('Task 3'), 800)),
() => new Promise(resolve => setTimeout(() => resolve('Task 4'), 1200)),
() => new Promise(resolve => setTimeout(() => resolve('Task 5'), 900))
];
const maxConcurrent = 2;
throttlePromises(asyncFunctions, maxConcurrent)
.then(results => {
console.log('All tasks completed');
console.log('Results:', results);
})
.catch(error => {
console.error('An error occurred:', error);
});
// All tasks completed
// Results: ['Task 1', 'Task 2', 'Task 3', 'Task 4', 'Task 5']
Traffic light
function red() {
console.log('red');
}
function green() {
console.log('green');
}
function yellow() {
console.log('yellow');
}
const colorActions = {
red,
green,
yellow,
}
function trafficLight(delay, color) {
return new Promise((resolve) => {
setTimeout(() => {
if (colorActions[color]) {
colorActions[color]();
} else {
console.warn(`Unknown color: ${color}`);
}
resolve();
}, delay);
});
}
// Usage example
async function trafficRunner() {
const trafficSequence = [
{ delay: 1000, color: 'red' },
{ delay: 2000, color: 'green' },
{ delay: 3000, color: 'yellow' },
]
while (true) {
for (const { delay, color } of trafficSequence) {
await trafficLight(delay, color);
}
}
}
trafficRunner();
Reference
- sleep (command) - Wikipedia.org
- Concurrency (computer science) - Wikipedia.org
- Scheduling (computing) - Wikipedia.org
- Asynchrony (computer programming) - Wikipedia.org
- 2723. Add Two Promises - LeetCode
- 2621. Sleep - LeetCode
- 2637. Promise Time Limit - LeetCode
- 29. implement async helper -
sequence()
- BFE.dev - 30. implement async helper -
parallel()
- BFE.dev - 31. implement async helper -
race()
- BFE.dev - 56. call APIs with pagination - BFE.dev
- 64. auto-retry Promise on rejection - BFE.dev
- 92. throttle Promises - BFE.dev
- 130. create LazyMan() - BFE.dev
- 159. implement promisify() - BFE.dev
- GreatFrontEnd