You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

164 lines
4.3 KiB
JavaScript

var isPromise = require('is-promise')
var nextTick
if (typeof setImediate === 'function') nextTick = setImediate
else if (typeof process === 'object' && process && process.nextTick) nextTick = process.nextTick
else nextTick = function (cb) { setTimeout(cb, 0) }
var extensions = []
module.exports = Promise
function Promise(fn) {
if (!(this instanceof Promise)) {
return fn ? new Promise(fn) : defer()
}
if (typeof fn !== 'function') {
throw new TypeError('fn is not a function')
}
var state = {
isResolved: false,
isSettled: false,
isFulfilled: false,
value: null,
waiting: [],
running: false
}
function _resolve(val) {
resolve(state, val);
}
function _reject(err) {
reject(state, err);
}
this.then = function _then(onFulfilled, onRejected) {
return then(state, onFulfilled, onRejected);
}
_resolve.fulfill = deprecate(_resolve, 'resolver.fulfill(x)', 'resolve(x)')
_resolve.reject = deprecate(_reject, 'resolver.reject', 'reject(x)')
try {
fn(_resolve, _reject)
} catch (ex) {
_reject(ex)
}
}
function resolve(promiseState, value) {
if (promiseState.isResolved) return
if (isPromise(value)) {
assimilate(promiseState, value)
} else {
settle(promiseState, true, value)
}
}
function reject(promiseState, reason) {
if (promiseState.isResolved) return
settle(promiseState, false, reason)
}
function then(promiseState, onFulfilled, onRejected) {
return new Promise(function (resolve, reject) {
function done(next, skipTimeout) {
var callback = promiseState.isFulfilled ? onFulfilled : onRejected
if (typeof callback === 'function') {
function timeoutDone() {
var val
try {
val = callback(promiseState.value)
} catch (ex) {
reject(ex)
return next(true)
}
resolve(val)
next(true)
}
if (skipTimeout) timeoutDone()
else nextTick(timeoutDone)
} else if (promiseState.isFulfilled) {
resolve(promiseState.value)
next(skipTimeout)
} else {
reject(promiseState.value)
next(skipTimeout)
}
}
promiseState.waiting.push(done)
if (promiseState.isSettled && !promiseState.running) processQueue(promiseState)
})
}
function processQueue(promiseState) {
function next(skipTimeout) {
if (promiseState.waiting.length) {
promiseState.running = true
promiseState.waiting.shift()(next, skipTimeout)
} else {
promiseState.running = false
}
}
next(false)
}
function settle(promiseState, isFulfilled, value) {
if (promiseState.isSettled) return
promiseState.isResolved = promiseState.isSettled = true
promiseState.value = value
promiseState.isFulfilled = isFulfilled
processQueue(promiseState)
}
function assimilate(promiseState, thenable) {
try {
promiseState.isResolved = true
thenable.then(function (res) {
if (isPromise(res)) {
assimilate(promiseState, res)
} else {
settle(promiseState, true, res)
}
}, function (err) {
settle(promiseState, false, err)
})
} catch (ex) {
settle(promiseState, false, ex)
}
}
Promise.use = function (extension) {
extensions.push(extension)
}
function deprecate(method, name, alternative) {
return function () {
var err = new Error(name + ' is deprecated use ' + alternative)
if (typeof console !== 'undefined' && console && typeof console.warn === 'function') {
console.warn(name + ' is deprecated use ' + alternative)
if (err.stack) console.warn(err.stack)
} else {
nextTick(function () {
throw err
})
}
method.apply(this, arguments)
}
}
function defer() {
var err = new Error('promise.defer() is deprecated')
if (typeof console !== 'undefined' && console && typeof console.warn === 'function') {
console.warn('promise.defer() is deprecated')
if (err.stack) console.warn(err.stack)
} else {
nextTick(function () {
throw err
})
}
var resolver
var promise = new Promise(function (res) { resolver = res })
return {resolver: resolver, promise: promise}
}