DEV Community

Mitchell
Mitchell

Posted on

Promise - JavaScript challenges

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
Enter fullscreen mode Exit fullscreen mode

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));
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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();
}
Enter fullscreen mode Exit fullscreen mode

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]
Enter fullscreen mode Exit fullscreen mode

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' }
  ]
*/
Enter fullscreen mode Exit fullscreen mode

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
*/
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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!'
  });
Enter fullscreen mode Exit fullscreen mode

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' }
}
*/
Enter fullscreen mode Exit fullscreen mode

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]
  });
Enter fullscreen mode Exit fullscreen mode

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]
*/
Enter fullscreen mode Exit fullscreen mode

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 ]
Enter fullscreen mode Exit fullscreen mode

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' }
]
*/
Enter fullscreen mode Exit fullscreen mode

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.
*/
Enter fullscreen mode Exit fullscreen mode

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
  });
Enter fullscreen mode Exit fullscreen mode

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
*/
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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 });
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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);
  });
Enter fullscreen mode Exit fullscreen mode

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)
*/
Enter fullscreen mode Exit fullscreen mode

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();
Enter fullscreen mode Exit fullscreen mode

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.
Enter fullscreen mode Exit fullscreen mode

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']
Enter fullscreen mode Exit fullscreen mode

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();
Enter fullscreen mode Exit fullscreen mode

Reference

Top comments (0)