Last active
April 6, 2019 02:37
-
-
Save Phoenix35/0699bb303fe868129ab24a0d7d310e65 to your computer and use it in GitHub Desktop.
Small utility function to delay async operations
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /** | |
| * prepareAsync - Wraps an asynchronous function to be called several times with a delay between each call. | |
| * @param {AsyncFunction} cb - The callback to wait for | |
| * @param {number} delay - The delay (in milliseconds) between each call | |
| * @param {Function} onFulfill - Function called after each success | |
| * @param {Function} onError - Function to handle errors thrown during the resolutions of `cb` AND `onFulfill` | |
| * @return {AsyncIterable} | |
| */ | |
| export default function prepareAsync (cb, delay, onFulfill, onError) { | |
| const sleep = { then (resolve) { | |
| setTimeout(resolve, delay); | |
| } }; | |
| /** | |
| * produceAsync - Asynchronously yields the result of the callback applied on each element of the iterable | |
| * and wait a defined amount of time before the next call. | |
| * @param {Iterable} it - The iterable to consume | |
| * @param {?Object} thisArg - The optional `this` to pass to each call | |
| * @param {?Function} done - Function to execute once the iterable is consumed | |
| * @return {*} - The return value of `done` | |
| */ | |
| return async function *produceAsync (it, thisArg, done = () => {}) { | |
| for (const x of it) { | |
| yield await Reflect.apply(cb, thisArg, x) | |
| .then(onFulfill) | |
| .catch(onError); | |
| await sleep; | |
| } | |
| return done(); | |
| }; | |
| } | |
| // The whole thing can be seen as a transform stream with delay | |
| /* | |
| Example with fetch | |
| */ | |
| function fetchSuccess (response) { | |
| if (!response.ok) | |
| throw new TypeError( | |
| `URL: ${response.url} ${response.status}: ${response.statusText}` | |
| ); | |
| const cType = response.headers.get("Content-Type"); | |
| const method = cType.startsWith("application/json") | |
| ? "json" | |
| : cType.startsWith("text/") | |
| ? "text" | |
| : "arraybuffer"; | |
| return response[method](); | |
| } | |
| function fetchError (err) { | |
| console.error(err); | |
| } | |
| const noSpamFetches = prepareAsync( | |
| fetch, | |
| 2000, // Remember, delay is in ms! | |
| fetchSuccess, | |
| fetchError, | |
| ); | |
| const baseURL = "https://jsonplaceholder.typicode.com/users/1"; | |
| const headers = { | |
| Accept: "application/json", | |
| }; | |
| // `.apply` expects an array-like object, i.e. `fetch.apply(null, [url, init])` | |
| const userAlbumsRequest = [ `${baseURL}/albums`, { headers } ]; | |
| const userTodosRequest = [ `${baseURL}/todos`, { headers } ]; | |
| const userFailRequest = [ `${baseURL}/fa1l`, { headers } ]; | |
| const userPostsRequest = [ `${baseURL}/posts`, { headers } ]; | |
| // This is just to show it accepts any iterable | |
| const userRequests = new Set([ userAlbumsRequest, userTodosRequest, userFailRequest, userPostsRequest ]); | |
| const aggregatedResults = []; | |
| for await (const result of noSpamFetches( | |
| userRequests, | |
| null, | |
| () => console.log("I is done!"), | |
| )) | |
| aggregatedResults.push(result); | |
| // This is just to be clear that order is preserved | |
| const [ userAlbums, userTodos, fetchErrorReturnsUndefined, userPosts ] = aggregatedResults; | |
| console.log(userAlbums, userTodos, fetchErrorReturnsUndefined, userPosts); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment