japji/resources/public/js/cljs-runtime/promesa.impl.promise.js
2025-09-01 12:49:14 +01:00

333 lines
8.8 KiB
JavaScript

goog.provide("promesa.impl.promise");
goog.provide("promesa.impl.promise.PromiseImpl");
goog.provide("promesa.impl.promise.CancellationError");
goog.scope(function() {
function isCancellationError(v) {
return v instanceof CancellationError;
}
function fmtValue(o) {
if (isThenable(o)) {
return `<PROMISE:${goog.getUid(o)}>`;
} else if (o instanceof Error) {
return `<EXCEPTION:'${o.message}'>`;
} else if (o === null || o === undefined) {
return `${o}`;
} else if (typeof o === "function") {
return `<FN:${goog.getUid(o)}>`;
} else {
return `${o.toString()}`;
}
}
function isSome(o) {
return o !== null && o !== undefined;
}
function isFunction(o) {
return typeof o === "function";
}
function isThenable(o) {
if (goog.isObject(o)) {
const thenFn = o.then;
return isFunction(thenFn);
} else {
return false;
}
}
function constantly(v) {
return () => {
return v;
};
}
function identity(v) {
return v;
}
function isPromiseImpl(v) {
return v instanceof PromiseImpl;
}
function completeDeferredFn(deferred) {
return (value, cause) => {
if (cause) {
deferred.reject(cause);
} else {
deferred.resolve(value);
}
};
}
function process(p) {
if (p[STATE] === PENDING) {
return;
}
nextTick(processNextTick, p);
return p;
}
function processNextTick(p) {
if (p[QUEUE].length === 0) {
return;
}
const state = p[STATE];
const value = p[VALUE];
let task;
let rvalue;
let rcause;
for (; p[QUEUE].length;) {
task = p[QUEUE].shift();
try {
if (state === RESOLVED) {
rvalue = task.resolve(value);
} else if (state === REJECTED) {
rvalue = task.reject(value);
} else {
rcause = new TypeError("invalid state");
}
} catch (e) {
rcause = e;
}
resolveTask(task, rvalue, rcause);
}
}
function resolveTask(task, value, cause) {
if (task.complete === undefined) {
return;
}
if (cause) {
task.complete(null, cause);
} else {
if (task.type === RESOLVE_TYPE_MAP) {
task.complete(value, null);
} else if (task.type === RESOLVE_TYPE_FLATTEN) {
if (isPromiseImpl(value)) {
value.handle((v, c) => {
resolveTask(task, v, c);
});
} else if (isThenable(value)) {
value.then(v => {
resolveTask(task, v, null);
}, c => {
resolveTask(task, null, c);
});
} else {
task.complete(value, null);
}
} else if (task.type === RESOLVE_TYPE_BIND) {
if (isPromiseImpl(value)) {
value.handle((v, c) => {
task.complete(v, c);
});
} else if (isThenable(value)) {
value.then(v => {
task.complete(v, null);
}, c => {
task.complete(null, c);
});
} else {
task.complete(null, new TypeError("expected thenable"));
}
} else {
task.complete(null, new TypeError("internal: invalid resolve type"));
}
}
}
function transition(p, state, value) {
if (p[STATE] === state || p[STATE] !== PENDING) {
return;
}
p[STATE] = state;
p[VALUE] = value;
return processNextTick(p);
}
const self = promesa.impl.promise;
const root = goog.global;
const PENDING = Symbol("state/pending");
const RESOLVED = Symbol("state/resolved");
const REJECTED = Symbol("state/rejected");
const QUEUE = Symbol("queue");
const STATE = Symbol("state");
const VALUE = Symbol("value");
const RESOLVE_TYPE_FLATTEN = Symbol("resolve-type/flatten");
const RESOLVE_TYPE_BIND = Symbol("resolve-type/bind");
const RESOLVE_TYPE_MAP = Symbol("resolve-type/map");
const defaultResolveMapHandler = v => {
return v;
};
const defaultResolveBindHandler = v => {
return self.resolved(v);
};
const defaultRejectHandler = c => {
throw c;
};
class CancellationError extends Error {
}
class PromiseImpl {
constructor(val) {
this[QUEUE] = [];
this[STATE] = PENDING;
this[VALUE] = undefined;
if (val !== undefined) {
transition(this, RESOLVED, val);
}
}
get state() {
return this[STATE];
}
get value() {
return this[VALUE];
}
then(resolve, reject) {
const deferred = new PromiseImpl();
this[QUEUE].push({type:RESOLVE_TYPE_FLATTEN, resolve:resolve ?? defaultResolveMapHandler, reject:reject ?? defaultRejectHandler, complete:completeDeferredFn(deferred)});
process(this);
return deferred;
}
catch(reject) {
return this.then(null, reject);
}
finally(f) {
this[QUEUE].push({type:RESOLVE_TYPE_FLATTEN, resolve:value => {
return f();
}, reject:cause => {
return f();
}, complete:(value, cause) => {
return null;
}});
return this;
}
fmap(resolve, reject) {
const deferred = new PromiseImpl();
this[QUEUE].push({type:RESOLVE_TYPE_MAP, resolve:resolve ?? defaultResolveMapHandler, reject:reject ?? defaultRejectHandler, complete:completeDeferredFn(deferred)});
process(this);
return deferred;
}
fbind(resolve, reject) {
const deferred = new PromiseImpl();
this[QUEUE].push({type:RESOLVE_TYPE_BIND, resolve:resolve ?? defaultResolveBindHandler, reject:reject ?? defaultRejectHandler, complete:completeDeferredFn(deferred)});
process(this);
return deferred;
}
handle(fn, resolveType) {
resolveType = resolveType ?? RESOLVE_TYPE_MAP;
this[QUEUE].push({type:resolveType, resolve:defaultResolveMapHandler, reject:defaultRejectHandler, complete:fn});
process(this);
}
resolve(value) {
if (this[STATE] === PENDING) {
transition(this, RESOLVED, value);
}
return null;
}
reject(cause) {
if (this[STATE] === PENDING) {
transition(this, REJECTED, cause);
}
return null;
}
isPending() {
const state = this[STATE];
return state === PENDING;
}
isResolved() {
const state = this[STATE];
return state === RESOLVED;
}
isRejected() {
const state = this[STATE];
return state === REJECTED;
}
isCancelled() {
const state = this[STATE];
const value = this[VALUE];
return state === REJECTED && isCancellationError(value);
}
cancel() {
this.reject(new CancellationError("promise cancelled"));
}
}
const nextTick = (() => {
if (typeof root.Promise === "function") {
const resolved = Promise.resolve(null);
return function queueMicrotaskWithPromise(f, p) {
resolved.then(() => {
return f(p);
});
};
} else if (typeof root.setImmediate === "function") {
return root.setImmediate;
} else if (typeof root.setTimeout === "function") {
return (f, p) => {
return root.setTimeout(f, 0, p);
};
} else {
return (f, p) => {
return f.call(this, p);
};
}
})();
self.PromiseImpl = PromiseImpl;
self.CancellationError = CancellationError;
self.isCancellationError = isCancellationError;
self.deferred = () => {
return new PromiseImpl();
};
const NULL_PROMISE = new PromiseImpl(null);
self.resolved = function resolved(value) {
if (value === null) {
return NULL_PROMISE;
} else {
const p = new PromiseImpl();
p[STATE] = RESOLVED;
p[VALUE] = value;
return p;
}
};
self.rejected = function rejected(reason) {
const p = new PromiseImpl();
p[STATE] = REJECTED;
p[VALUE] = reason;
return p;
};
self.all = function all(promises) {
return promises.reduce((acc, p) => {
return acc.then(results => {
return self.coerce(p).fmap(v => {
results.push(v);
return results;
});
});
}, self.resolved([]));
};
self.coerce = function coerce(promise) {
if (promise instanceof PromiseImpl) {
return promise;
} else if (isThenable(promise)) {
const deferred = self.deferred();
promise.then(v => {
deferred.resolve(v);
}, c => {
deferred.reject(c);
});
return deferred;
} else if (promise instanceof Error) {
return self.rejected(promise);
} else {
return self.resolved(promise);
}
};
self.race = function race(promises) {
const deferred = self.deferred();
promises.forEach(p => {
self.coerce(p).handle((v, c) => {
if (c) {
deferred.reject(c);
} else {
deferred.resolve(v);
}
});
});
return deferred;
};
self.nextTick = nextTick;
self.PENDING = PENDING;
self.RESOLVED = RESOLVED;
self.REJECTED = REJECTED;
});
//# sourceMappingURL=promesa.impl.promise.js.map