123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473 |
- /* jshint node: true */
- "use strict";
- function makeArrayFrom(obj) {
- return Array.prototype.slice.apply(obj);
- }
- var
- PENDING = "pending",
- RESOLVED = "resolved",
- REJECTED = "rejected";
- function SynchronousPromise(handler) {
- this.status = PENDING;
- this._continuations = [];
- this._parent = null;
- this._paused = false;
- if (handler) {
- handler.call(
- this,
- this._continueWith.bind(this),
- this._failWith.bind(this)
- );
- }
- }
- function looksLikeAPromise(obj) {
- return obj && typeof (obj.then) === "function";
- }
- function passThrough(value) {
- return value;
- }
- SynchronousPromise.prototype = {
- then: function (nextFn, catchFn) {
- var next = SynchronousPromise.unresolved()._setParent(this);
- if (this._isRejected()) {
- if (this._paused) {
- this._continuations.push({
- promise: next,
- nextFn: nextFn,
- catchFn: catchFn
- });
- return next;
- }
- if (catchFn) {
- try {
- var catchResult = catchFn(this._error);
- if (looksLikeAPromise(catchResult)) {
- this._chainPromiseData(catchResult, next);
- return next;
- } else {
- return SynchronousPromise.resolve(catchResult)._setParent(this);
- }
- } catch (e) {
- return SynchronousPromise.reject(e)._setParent(this);
- }
- }
- return SynchronousPromise.reject(this._error)._setParent(this);
- }
- this._continuations.push({
- promise: next,
- nextFn: nextFn,
- catchFn: catchFn
- });
- this._runResolutions();
- return next;
- },
- catch: function (handler) {
- if (this._isResolved()) {
- return SynchronousPromise.resolve(this._data)._setParent(this);
- }
- var next = SynchronousPromise.unresolved()._setParent(this);
- this._continuations.push({
- promise: next,
- catchFn: handler
- });
- this._runRejections();
- return next;
- },
- finally: function (callback) {
- var ran = false;
- function runFinally(result, err) {
- if (!ran) {
- ran = true;
- if (!callback) {
- callback = passThrough;
- }
- var callbackResult = callback(result);
- if (looksLikeAPromise(callbackResult)) {
- return callbackResult.then(function () {
- if (err) {
- throw err;
- }
- return result;
- });
- } else {
- return result;
- }
- }
- }
- return this
- .then(function (result) {
- return runFinally(result);
- })
- .catch(function (err) {
- return runFinally(null, err);
- });
- },
- pause: function () {
- this._paused = true;
- return this;
- },
- resume: function () {
- var firstPaused = this._findFirstPaused();
- if (firstPaused) {
- firstPaused._paused = false;
- firstPaused._runResolutions();
- firstPaused._runRejections();
- }
- return this;
- },
- _findAncestry: function () {
- return this._continuations.reduce(function (acc, cur) {
- if (cur.promise) {
- var node = {
- promise: cur.promise,
- children: cur.promise._findAncestry()
- };
- acc.push(node);
- }
- return acc;
- }, []);
- },
- _setParent: function (parent) {
- if (this._parent) {
- throw new Error("parent already set");
- }
- this._parent = parent;
- return this;
- },
- _continueWith: function (data) {
- var firstPending = this._findFirstPending();
- if (firstPending) {
- firstPending._data = data;
- firstPending._setResolved();
- }
- },
- _findFirstPending: function () {
- return this._findFirstAncestor(function (test) {
- return test._isPending && test._isPending();
- });
- },
- _findFirstPaused: function () {
- return this._findFirstAncestor(function (test) {
- return test._paused;
- });
- },
- _findFirstAncestor: function (matching) {
- var test = this;
- var result;
- while (test) {
- if (matching(test)) {
- result = test;
- }
- test = test._parent;
- }
- return result;
- },
- _failWith: function (error) {
- var firstRejected = this._findFirstPending();
- if (firstRejected) {
- firstRejected._error = error;
- firstRejected._setRejected();
- }
- },
- _takeContinuations: function () {
- return this._continuations.splice(0, this._continuations.length);
- },
- _runRejections: function () {
- if (this._paused || !this._isRejected()) {
- return;
- }
- var
- error = this._error,
- continuations = this._takeContinuations(),
- self = this;
- continuations.forEach(function (cont) {
- if (cont.catchFn) {
- try {
- var catchResult = cont.catchFn(error);
- self._handleUserFunctionResult(catchResult, cont.promise);
- } catch (e) {
- cont.promise.reject(e);
- }
- } else {
- cont.promise.reject(error);
- }
- });
- },
- _runResolutions: function () {
- if (this._paused || !this._isResolved() || this._isPending()) {
- return;
- }
- var continuations = this._takeContinuations();
- var data = this._data;
- var self = this;
- continuations.forEach(function (cont) {
- if (cont.nextFn) {
- try {
- var result = cont.nextFn(data);
- self._handleUserFunctionResult(result, cont.promise);
- } catch (e) {
- self._handleResolutionError(e, cont);
- }
- } else if (cont.promise) {
- cont.promise.resolve(data);
- }
- });
- if (looksLikeAPromise(this._data)) {
- return this._handleWhenResolvedDataIsPromise(this._data);
- }
- },
- _handleResolutionError: function (e, continuation) {
- this._setRejected();
- if (continuation.catchFn) {
- try {
- continuation.catchFn(e);
- return;
- } catch (e2) {
- e = e2;
- }
- }
- if (continuation.promise) {
- continuation.promise.reject(e);
- }
- },
- _handleWhenResolvedDataIsPromise: function (data) {
- var self = this;
- return data.then(function (result) {
- self._data = result;
- self._runResolutions();
- }).catch(function (error) {
- self._error = error;
- self._setRejected();
- self._runRejections();
- });
- },
- _handleUserFunctionResult: function (data, nextSynchronousPromise) {
- if (looksLikeAPromise(data)) {
- this._chainPromiseData(data, nextSynchronousPromise);
- } else {
- nextSynchronousPromise.resolve(data);
- }
- },
- _chainPromiseData: function (promiseData, nextSynchronousPromise) {
- promiseData.then(function (newData) {
- nextSynchronousPromise.resolve(newData);
- }).catch(function (newError) {
- nextSynchronousPromise.reject(newError);
- });
- },
- _setResolved: function () {
- this.status = RESOLVED;
- if (!this._paused) {
- this._runResolutions();
- }
- },
- _setRejected: function () {
- this.status = REJECTED;
- if (!this._paused) {
- this._runRejections();
- }
- },
- _isPending: function () {
- return this.status === PENDING;
- },
- _isResolved: function () {
- return this.status === RESOLVED;
- },
- _isRejected: function () {
- return this.status === REJECTED;
- }
- };
- SynchronousPromise.resolve = function (result) {
- return new SynchronousPromise(function (resolve, reject) {
- if (looksLikeAPromise(result)) {
- result.then(function (newResult) {
- resolve(newResult);
- }).catch(function (error) {
- reject(error);
- });
- } else {
- resolve(result);
- }
- });
- };
- SynchronousPromise.reject = function (result) {
- return new SynchronousPromise(function (resolve, reject) {
- reject(result);
- });
- };
- SynchronousPromise.unresolved = function () {
- return new SynchronousPromise(function (resolve, reject) {
- this.resolve = resolve;
- this.reject = reject;
- });
- };
- SynchronousPromise.all = function () {
- var args = makeArrayFrom(arguments);
- if (Array.isArray(args[0])) {
- args = args[0];
- }
- if (!args.length) {
- return SynchronousPromise.resolve([]);
- }
- return new SynchronousPromise(function (resolve, reject) {
- var
- allData = [],
- numResolved = 0,
- doResolve = function () {
- if (numResolved === args.length) {
- resolve(allData);
- }
- },
- rejected = false,
- doReject = function (err) {
- if (rejected) {
- return;
- }
- rejected = true;
- reject(err);
- };
- args.forEach(function (arg, idx) {
- SynchronousPromise.resolve(arg).then(function (thisResult) {
- allData[idx] = thisResult;
- numResolved += 1;
- doResolve();
- }).catch(function (err) {
- doReject(err);
- });
- });
- });
- };
- function createAggregateErrorFrom(errors) {
- /* jshint ignore:start */
- if (typeof window !== "undefined" && "AggregateError" in window) {
- return new window.AggregateError(errors);
- }
- /* jshint ignore:end */
- return { errors: errors };
- }
- SynchronousPromise.any = function () {
- var args = makeArrayFrom(arguments);
- if (Array.isArray(args[0])) {
- args = args[0];
- }
- if (!args.length) {
- return SynchronousPromise.reject(createAggregateErrorFrom([]));
- }
- return new SynchronousPromise(function (resolve, reject) {
- var
- allErrors = [],
- numRejected = 0,
- doReject = function () {
- if (numRejected === args.length) {
- reject(createAggregateErrorFrom(allErrors));
- }
- },
- resolved = false,
- doResolve = function (result) {
- if (resolved) {
- return;
- }
- resolved = true;
- resolve(result);
- };
- args.forEach(function (arg, idx) {
- SynchronousPromise.resolve(arg).then(function (thisResult) {
- doResolve(thisResult);
- }).catch(function (err) {
- allErrors[idx] = err;
- numRejected += 1;
- doReject();
- });
- });
- });
- };
- SynchronousPromise.allSettled = function () {
- var args = makeArrayFrom(arguments);
- if (Array.isArray(args[0])) {
- args = args[0];
- }
- if (!args.length) {
- return SynchronousPromise.resolve([]);
- }
- return new SynchronousPromise(function (resolve) {
- var
- allData = [],
- numSettled = 0,
- doSettled = function () {
- numSettled += 1;
- if (numSettled === args.length) {
- resolve(allData);
- }
- };
- args.forEach(function (arg, idx) {
- SynchronousPromise.resolve(arg).then(function (thisResult) {
- allData[idx] = {
- status: "fulfilled",
- value: thisResult
- };
- doSettled();
- }).catch(function (err) {
- allData[idx] = {
- status: "rejected",
- reason: err
- };
- doSettled();
- });
- });
- });
- };
- /* jshint ignore:start */
- if (Promise === SynchronousPromise) {
- throw new Error("Please use SynchronousPromise.installGlobally() to install globally");
- }
- var RealPromise = Promise;
- SynchronousPromise.installGlobally = function (__awaiter) {
- if (Promise === SynchronousPromise) {
- return __awaiter;
- }
- var result = patchAwaiterIfRequired(__awaiter);
- Promise = SynchronousPromise;
- return result;
- };
- SynchronousPromise.uninstallGlobally = function () {
- if (Promise === SynchronousPromise) {
- Promise = RealPromise;
- }
- };
- function patchAwaiterIfRequired(__awaiter) {
- if (typeof (__awaiter) === "undefined" || __awaiter.__patched) {
- return __awaiter;
- }
- var originalAwaiter = __awaiter;
- __awaiter = function () {
- var Promise = RealPromise;
- originalAwaiter.apply(this, makeArrayFrom(arguments));
- };
- __awaiter.__patched = true;
- return __awaiter;
- }
- /* jshint ignore:end */
- module.exports = {
- SynchronousPromise: SynchronousPromise
- };
|