You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							473 lines
						
					
					
						
							11 KiB
						
					
					
				
			
		
		
	
	
							473 lines
						
					
					
						
							11 KiB
						
					
					
				/* 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(); | 
						|
    if (looksLikeAPromise(this._data)) { | 
						|
      return this._handleWhenResolvedDataIsPromise(this._data); | 
						|
    } | 
						|
    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); | 
						|
      } | 
						|
    }); | 
						|
  }, | 
						|
  _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 | 
						|
};
 | 
						|
 |