UNPKG

vue-concurrency

Version:

A library for encapsulating asynchronous operations and managing concurrency for Vue + Composition API

341 lines (340 loc) 9.87 kB
import { CAF as p } from "caf"; import { watch as y, computed as u, reactive as g, effectScope as b, getCurrentInstance as m, onBeforeUnmount as w, onServerPrefetch as O } from "vue"; function U(n) { return new Promise((r) => { const t = y(n, (s) => { s != null && (r(s), t && t()); }, { immediate: !0 }); }); } const R = (n) => n._runningInstances.length >= n._maxConcurrency, D = (n) => { const r = n._activeInstances[0]; r && r.cancel(); }, F = (n) => { n._enqueuedInstances.forEach((r) => { r.isEnqueued = !1, r.isDropped = !0; }); }; function l(n, r) { return r ? x(() => n()._instances, r) : u(() => []); } function x(n, r, t) { return u(() => n().filter((e) => { const c = e[r]; return t ? c === t : c; })); } function S(n) { return u(() => n().length); } function h(n) { return u(() => { const r = n(); return r[r.length - 1]; }); } function j(n) { return u(() => n()[0]); } const E = (n) => n; function v(n) { return g(n); } function L() { const n = {}, r = new Promise((t, s) => { n.resolve = t, n.reject = s; }); return n.promise = r, n; } function B(n) { let r = "General"; n._isDropping && (r = "Drop"), n._isEnqueuing && (r = "Enqueue"), n._isRestartable && (r = "Restartable"), n._isKeepingLatest && (r = "KeepLatest"); let t = `${r} Task`; r !== "General" && (t = `${t} with maxConcurrency ${n._maxConcurrency}`); const s = n._instances.map((e) => { let c; e.isSuccessful ? c = "🍏" : e.isRunning || e.isEnqueued ? c = "🍊" : (e.isError || e.isCanceled || e.isDropped) && (c = "🔴"); const { status: i, value: o, error: a } = e; return { status: `${c} ${i}`, value: o, error: a }; }); console.log(`🚦 ${t}`), console.table(s); } function V(n) { return process.env.NODE_ENV === "test" ? Promise.resolve() : new Promise((r) => setTimeout(r, n)); } function X(n, r) { return new n.CancelToken((t) => { r.pr.catch((s) => { s === "cancel" && t(); }); }); } function k(n) { return f(function* (r, ...t) { return n(r, ...t); }); } function P(n, r, t) { const s = E({ id: t.id, isDropped: !1, isEnqueued: !1, hasStarted: !1, isRunning: !1, isFinished: !1, isCanceling: !1, isCanceled: u( () => e.isCanceling && e.isFinished ), isActive: u( () => e.isRunning && !e.isCanceling ), isSuccessful: !1, isNotDropped: u(() => !e.isDropped), isError: u(() => !!e.error), status: u(() => { const i = e, o = [ [i.isRunning, "running"], [i.isEnqueued, "enqueued"], [i.isCanceled, "canceled"], [i.isCanceling, "canceling"], [i.isDropped, "dropped"], [i.isError, "error"], [i.isSuccessful, "success"] ].find(([a]) => a); return o && o[1]; }), error: null, value: null, cancel({ force: i } = { force: !1 }) { if (i || (e.isCanceling = !0, e.isEnqueued && (e.isFinished = !0), e.isEnqueued = !1), e.token && e._canAbort) { e.token.abort("cancel"); try { e.token.discard(); } catch { } e.token = void 0, e._canAbort = !1; } }, canceledOn(i) { return i.pr.catch(() => { e.cancel(); }), e; }, _run() { A(e, n, r, t); }, // PromiseLike things. These are necessary so that TaskInstance is `then`able and can be `await`ed // Workaround for Vue not to scream because of unhandled rejection. Task is always "handled" because the error is saved to taskInstance.error. _handled: !0, _deferredObject: L(), _shouldThrow: !1, // task throws only if it's used promise-like way (then, catch, await) _canAbort: !0, then(i, o) { return e._shouldThrow = !0, e._deferredObject.promise.then(i, o); }, catch(i, o = !0) { return e._shouldThrow = o, e._deferredObject.promise.catch(i); }, finally(i) { return e._shouldThrow = !0, e._deferredObject.promise.finally(i); } }), e = v(s), { modifiers: c } = t; return c.drop ? e.isDropped = !0 : c.enqueue ? e.isEnqueued = !0 : e._run(), e; } function A(n, r, t, s) { const e = new p.cancelToken(), c = p(r, e); n.token = e, n.hasStarted = !0, n.isRunning = !0, n.isEnqueued = !1; function i() { n.isRunning = !1, n.isFinished = !0; } c.call(n, e, ...t).then((o) => { n.value = o, n.isSuccessful = !0, i(), n._deferredObject.resolve(o), n._canAbort = !1, s.onFinish(n); }).catch((o) => { o !== "cancel" && (n.error = o), i(), n._shouldThrow && n._deferredObject.reject(o), s.onFinish(n); }); } function f(n, r = { cancelOnUnmount: !0 }) { const t = b(), s = E({ _isRestartable: !1, _isDropping: !1, _isEnqueuing: !1, _isKeepingLatest: !1, _maxConcurrency: 1, // this is used only when concurrency modifier is active (otherwise it has no effect) _hasConcurrency: u( () => e._isRestartable || e._isDropping || e._isEnqueuing || e._isKeepingLatest ), isIdle: u(() => !e.isRunning), isRunning: u( () => !!e._instances.find((c) => c.isRunning) ), isError: u(() => !!(e.last && e.last.isError)), _instances: [], // TODO: the filter + lastOf combo is concise and clear, but more efficient would be classic loop and iterating from the end (findLastIf macro) _successfulInstances: l(() => e, "isSuccessful"), _runningInstances: l(() => e, "isRunning"), _enqueuedInstances: l(() => e, "isEnqueued"), _notDroppedInstances: l(() => e, "isNotDropped"), _activeInstances: l(() => e, "isActive"), performCount: S(() => e._instances), last: h(() => e._notDroppedInstances), lastSuccessful: h(() => e._successfulInstances), firstEnqueued: j(() => e._enqueuedInstances), cancelAll({ force: c } = { force: !1 }) { e._instances.forEach( (i) => { try { (c || !i.isDropped && !i.isFinished) && i.cancel({ force: c }); } catch (o) { if (o !== "cancel") throw o; } } ); }, perform(...c) { const i = { enqueue: !1, drop: !1 }; e._hasConcurrency && R(e) && (e._isDropping && (i.drop = !0), e._isRestartable && D(e), e._isKeepingLatest && F(e), (e._isEnqueuing || e._isKeepingLatest) && (i.enqueue = !0)); const o = () => K(e), a = () => P(n, c, { modifiers: i, onFinish: o, scope: t, id: e._instances.length + 1 }), _ = t.active ? t.run(a) : a(); return e._instances = [...e._instances, _], _; }, clear() { this.cancelAll({ force: !0 }), this._instances = []; }, destroy() { t.stop(), this.clear(); }, restartable() { return e._resetModifierFlags(), e._isRestartable = !0, e; }, drop() { return e._resetModifierFlags(), e._isDropping = !0, e; }, enqueue() { return e._resetModifierFlags(), e._isEnqueuing = !0, e; }, keepLatest() { return e._resetModifierFlags(), e._isKeepingLatest = !0, e; }, _resetModifierFlags() { e._isKeepingLatest = !1, e._isRestartable = !1, e._isEnqueuing = !1, e._isDropping = !1; }, maxConcurrency(c) { return e._maxConcurrency = c, e; } }), e = v(s); return r.cancelOnUnmount && m() && w(() => { e._instances && e.destroy(); }), e; } function K(n) { if (n._isEnqueuing || n._isKeepingLatest) { const { firstEnqueued: r } = n; r && r._run(); } } function z(n, ...r) { return f(function* (t, ...s) { let e = yield n.perform(...s).canceledOn(t); for (let c of r) e = yield c.perform(e).canceledOn(t); return e; }); } function H(...n) { return f(function* (r, ...t) { const s = n.map((c) => c.perform(...t).canceledOn(r)); return yield Promise.all(s); }); } function J(...n) { return f(function* (r, ...t) { const s = []; for (let e of n) s.push(yield e.perform(...t).canceledOn(r)); return s; }); } const C = () => typeof window > "u"; function N(n) { n.isError ? n._deferredObject.promise = Promise.reject(n.error) : n._deferredObject.promise = Promise.resolve(n.value), n.cancel = () => { }, n.canceledOn = () => n, n._run = () => { }, n.then = (...r) => n._deferredObject.promise.then(...r), n.catch = (...r) => n._deferredObject.promise.catch(...r), n.finally = (...r) => n._deferredObject.promise.finally(...r); } function Q(n, r) { if (C()) { const s = r.perform(); return O(async () => { try { await s, T(n, r); } catch { } }), s; } const [t] = q(n, r).reverse(); return t || r.perform(); } function T(n, r) { const { $root: t } = m(), s = t && t.context && t.context.nuxtState; if (!s) throw new Error("Could not access $root.context.nuxtState"); s.vueConcurrency || (s.vueConcurrency = {}), s.vueConcurrency[n] = u(() => ({ instances: r._instances })); } function q(n, r) { const t = $(n); return t && (r._instances = t.instances || [], r._instances.forEach(N), I(n)), r._instances; } function d() { return window.__NUXT__; } function $(n) { if (!d()) throw Error("Could not access window.__NUXT__"); return d().vueConcurrency[n].value; } function I(n) { const r = d(); delete r.vueConcurrency[n]; } function W(n, r) { if (C()) { T(n, r); return; } q(n, r); } function Y(n) { const r = Object.values(n), t = u(() => !!r.find((e) => e.isRunning)); return g({ isRunning: t, isIdle: u(() => !t.value), isError: u(() => !!r.find((e) => e.isError)), ...n }); } export { X as getCancelToken, B as printTask, V as timeout, k as useAsyncTask, H as useParallelTask, z as usePipeTask, W as useSSRPersistance, J as useSequentialTask, f as useTask, Y as useTaskGroup, Q as useTaskPrefetch, U as waitForValue };