vue-concurrency
Version:
A library for encapsulating asynchronous operations and managing concurrency for Vue + Composition API
341 lines (340 loc) • 9.87 kB
JavaScript
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
};