kahuna/public/js/util/async.js (73 lines of code) (raw):

import angular from "angular"; import PQueue from "p-queue"; export var async = angular.module("util.async", []); /** * Return a lazy function that will yield before calling the input `func`. */ async.factory("nextTick", [ "$timeout", function($timeout) { function nextTick(func) { return () => $timeout(func, 0); } return nextTick; } ]); /** * Return a Promise that is resolved with no value after `duration`. */ async.factory("delay", [ "$q", "$timeout", function($q, $timeout) { function delay(duration) { var defer = $q.defer(); $timeout(defer.resolve, duration); return defer.promise; } return delay; } ]); async.factory("race", [ "$q", function($q) { function race(promises) { var first = $q.defer(); promises.forEach(promise => { promise.then(first.resolve, first.reject); }); return first.promise; } return race; } ]); const queue = new PQueue({ concurrency: 10 }); async.factory("apiPoll", [ "$q", ($q) => { const wait = (timeout) => new Promise(resolve => { setTimeout(() => resolve(), timeout); }); const poll = async (func, n) => { const [{ status, value }] = await Promise.allSettled([ queue.add(async () => { return await func(); }) ]); if (status === 'fulfilled') { return $q.resolve(value); } // Something has gone wrong, so we can let the user know if (n > 100) { throw new Error('gave up after 100 tries (apiPoll failed)'); } await wait(500 + n * 10); return poll(func, n + 1); }; return func => poll(func, 1); } ]); // Return a promise resolved the next time the event is fired on the scope async.factory("onNextEvent", [ "$q", function($q) { function onNextEvent(scope, event) { const defer = $q.defer(); const unregister = scope.$on(event, (_, arg) => defer.resolve(arg)); return defer.promise.finally(unregister); } return onNextEvent; } ]);