Skip to content

Instantly share code, notes, and snippets.

@avanavana
Last active February 6, 2021 19:34
Show Gist options
  • Save avanavana/6cd230656ed2463db2ad83d323eeeb88 to your computer and use it in GitHub Desktop.
Save avanavana/6cd230656ed2463db2ad83d323eeeb88 to your computer and use it in GitHub Desktop.
Demonstrates two methods for iterating over arrays of data sequentially, while applying asynchronous functions to each element. The first, queueAsync(), is to be used with Array.prototype.map(), or any functor, while the second, loopAsync(), is to be used with non-functor, non-mapping 'for'-type loops.
/**
* @file Async Iteration Demonstration
* @author Avana Vana <dear.avana@gmail.com>
* @version 1.2.0
*/
/**
* Simple sleep function, for simulating asynchronous operations
*
* @function sleep
* @param {number} s - The number of seconds to sleep for
* @returns {Promise} Resolved after {@link s} number of seconds
*/
const sleep = s => new Promise(r => setTimeout(r, s * 1000));
/**
* Utility async function which sleeps for a random number of seconds between 1-5 and then returns an object containing a value and its sleep duration
*
* @async
* @function randomSleepThenValue
* @param {*} val - A value to pass and log after 1-5 seconds
* @returns {Object} An object containing the value passed and duration slept before returning it
*/
const randomSleepThenValue = async (val, i) => {
const s = Math.ceil(Math.random() * 5);
await sleep(s);
console.log(`${i + 1}.) '${val}' done.`);
return { value: val, duration: s };
}
/**
* Allows a functor to be mapped sequentially, even if it contains asynchronous calls.
*
* @async
* @function queueAsync
* @param {Array} functor - An array of anything, a functor, something mappable (e.g. Array.prototype.map())
* @returns {Array} An array of values accumulated in sequence from each async function performed on the functor
*/
const queueAsync = async (functor) => {
const res = [];
functor.length > 1
? await functor.reduce((a, c, i, { length }) =>
(i === 1 ? a() : a).then((val) => {
res.push(val);
return i === length - 1 ? c().then((val) => res.push(val)) : c()}))
: await functor[0]().then((val) => res.push(val));
return res;
}
// Passing a custom, anonymous async function to Array.prototype.map() (same as randomSleepThenValue() above)
const queueAsyncResults = await queueAsync(['a', 'b', 'c'].map((val, i) => async () => {
const s = Math.ceil(Math.random() * 5);
await sleep(s);
console.log(`${i + 1}.) '${val}' done.`);
return { value: val, duration: s };
}));
// Passing a named async function to Array.prototype.map()
// const queueAsyncResults = await queueAsync(['a', 'b', 'c'].map((val, i) => () => randomSleepThenValue(val, i)));
console.log(queueAsyncResults);
// console:
// (1s) 1.) 'a' done.
// (6s) 2.) 'b' done.
// (7s) 3.) 'c' done.
// (7s) Array (3) [ { value: 'a', duration: 1 }, { value: 'b', duration: 5 }, { value: 'c', duration: 1 } ]
/**
* Loops over an array, sequentially performing an async operation for each item, and returns an ordered result
*
* @async
* @function loopAsync
* @param {Array} items - An array of anything, but not a functor with async operations (for that, use {@link queueAsync}
* @returns {Array} An array of values sequentially accumulated from each asynchronous operation in the loop
*/
const loopAsync = async (items) => {
const res = [];
for await (let [i, val] of items.entries()) {
const s = Math.ceil(Math.random() * 5);
await sleep(s);
console.log(`${i + 1}.) '${val}' done.`);
res.push({ value: val, duration: s });
}
return res;
}
const loopAsyncResults = await loopAsync(['a', 'b', 'c']);
console.log(loopAsyncResults);
// console:
// (2s) 1.) 'a' done.
// (6s) 2.) 'b' done.
// (9s) 3.) 'c' done.
// (9s) Array (3) [ { value: 'a', duration: 2 }, { value: 'b', duration: 4 }, { value: 'c', duration: 3 } ]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment