Skip to main content
版本:v1.0

Promise

;(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined'
? factory()
: typeof define === 'function' && define.amd
? define(factory)
: factory()
})(globalThis, function () {
'use strict'

/**
* @this {Promise}
*/
function finallyConstructor(callback) {
var constructor = this.constructor
return this.then(
function (value) {
// @ts-ignore
return constructor.resolve(callback()).then(function () {
return value
})
},
function (reason) {
// @ts-ignore
return constructor.resolve(callback()).then(function () {
// @ts-ignore
return constructor.reject(reason)
})
}
)
}

function allSettled(arr) {
var P = this
return new P(function (resolve, reject) {
if (!(arr && typeof arr.length !== 'undefined')) {
return reject(
new TypeError(
typeof arr +
' ' +
arr +
' is not iterable(cannot read property Symbol(Symbol.iterator))'
)
)
}
var args = Array.prototype.slice.call(arr)
if (args.length === 0) return resolve([])
var remaining = args.length

function res(i, val) {
if (val && (typeof val === 'object' || typeof val === 'function')) {
var then = val.then
if (typeof then === 'function') {
then.call(
val,
function (val) {
res(i, val)
},
function (e) {
args[i] = { status: 'rejected', reason: e }
if (--remaining === 0) {
resolve(args)
}
}
)
return
}
}
args[i] = { status: 'fulfilled', value: val }
if (--remaining === 0) {
resolve(args)
}
}

for (var i = 0; i < args.length; i++) {
res(i, args[i])
}
})
}

// Store setTimeout reference so promise-polyfill will be unaffected by
// other code modifying setTimeout (like sinon.useFakeTimers())
var setTimeoutFunc = setTimeout

function isArray(x) {
return Boolean(x && typeof x.length !== 'undefined')
}

function noop() {}

// Polyfill for Function.prototype.bind
function bind(fn, thisArg) {
return function () {
fn.apply(thisArg, arguments)
}
}

/**
* @constructor
* @param {Function} fn
*/
function Promise(fn) {
if (!(this instanceof Promise)) throw new TypeError('Promises must be constructed via new')
if (typeof fn !== 'function') throw new TypeError('not a function')
/** @type {!number} */
this._state = 0
/** @type {!boolean} */
this._handled = false
/** @type {Promise|undefined} */
this._value = undefined
/** @type {!Array<!Function>} */
this._deferreds = []

doResolve(fn, this)
}

function handle(self, deferred) {
while (self._state === 3) {
self = self._value
}
if (self._state === 0) {
self._deferreds.push(deferred)
return
}
self._handled = true
Promise._immediateFn(function () {
var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected
if (cb === null) {
;(self._state === 1 ? resolve : reject)(deferred.promise, self._value)
return
}
var ret
try {
ret = cb(self._value)
} catch (e) {
reject(deferred.promise, e)
return
}
resolve(deferred.promise, ret)
})
}

function resolve(self, newValue) {
try {
// Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure
if (newValue === self) throw new TypeError('A promise cannot be resolved with itself.')
if (newValue && (typeof newValue === 'object' || typeof newValue === 'function')) {
var then = newValue.then
if (newValue instanceof Promise) {
self._state = 3
self._value = newValue
finale(self)
return
} else if (typeof then === 'function') {
doResolve(bind(then, newValue), self)
return
}
}
self._state = 1
self._value = newValue
finale(self)
} catch (e) {
reject(self, e)
}
}

function reject(self, newValue) {
self._state = 2
self._value = newValue
finale(self)
}

function finale(self) {
if (self._state === 2 && self._deferreds.length === 0) {
Promise._immediateFn(function () {
if (!self._handled) {
Promise._unhandledRejectionFn(self._value)
}
})
}

for (var i = 0, len = self._deferreds.length; i < len; i++) {
handle(self, self._deferreds[i])
}
self._deferreds = null
}

/**
* @constructor
*/
function Handler(onFulfilled, onRejected, promise) {
this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null
this.onRejected = typeof onRejected === 'function' ? onRejected : null
this.promise = promise
}

/**
* Take a potentially misbehaving resolver function and make sure
* onFulfilled and onRejected are only called once.
*
* Makes no guarantees about asynchrony.
*/
function doResolve(fn, self) {
var done = false
try {
fn(
function (value) {
if (done) return
done = true
resolve(self, value)
},
function (reason) {
if (done) return
done = true
reject(self, reason)
}
)
} catch (ex) {
if (done) return
done = true
reject(self, ex)
}
}

Promise.prototype['catch'] = function (onRejected) {
return this.then(null, onRejected)
}

Promise.prototype.then = function (onFulfilled, onRejected) {
// @ts-ignore
var prom = new this.constructor(noop)

handle(this, new Handler(onFulfilled, onRejected, prom))
return prom
}

Promise.prototype['finally'] = finallyConstructor

Promise.all = function (arr) {
return new Promise(function (resolve, reject) {
if (!isArray(arr)) {
return reject(new TypeError('Promise.all accepts an array'))
}

var args = Array.prototype.slice.call(arr)
if (args.length === 0) return resolve([])
var remaining = args.length

function res(i, val) {
try {
if (val && (typeof val === 'object' || typeof val === 'function')) {
var then = val.then
if (typeof then === 'function') {
then.call(
val,
function (val) {
res(i, val)
},
reject
)
return
}
}
args[i] = val
if (--remaining === 0) {
resolve(args)
}
} catch (ex) {
reject(ex)
}
}

for (var i = 0; i < args.length; i++) {
res(i, args[i])
}
})
}

Promise.allSettled = allSettled

Promise.resolve = function (value) {
if (value && typeof value === 'object' && value.constructor === Promise) {
return value
}

return new Promise(function (resolve) {
resolve(value)
})
}

Promise.reject = function (value) {
return new Promise(function (resolve, reject) {
reject(value)
})
}

Promise.race = function (arr) {
return new Promise(function (resolve, reject) {
if (!isArray(arr)) {
return reject(new TypeError('Promise.race accepts an array'))
}

for (var i = 0, len = arr.length; i < len; i++) {
Promise.resolve(arr[i]).then(resolve, reject)
}
})
}

// Use polyfill for setImmediate for performance gains
Promise._immediateFn =
// @ts-ignore
(typeof setImmediate === 'function' &&
function (fn) {
// @ts-ignore
setImmediate(fn)
}) ||
function (fn) {
setTimeoutFunc(fn, 0)
}

Promise._unhandledRejectionFn = function _unhandledRejectionFn(err) {
if (typeof console !== 'undefined' && console) {
console.log('[jsfwk.error ] Possible Unhandled Promise Rejection:', err) // eslint-disable-line no-console
}
}

/** @suppress {undefinedVars} */
var globalNS = (function () {
// the only reliable means to get the global object is
// `Function('return this')()`
// However, this causes CSP violations in Chrome apps.
if (typeof self !== 'undefined') {
return self
}
if (typeof window !== 'undefined') {
return window
}
if (typeof global !== 'undefined') {
return global
}
if (typeof globalThis !== 'undefined') {
return globalThis
}

throw new Error('unable to locate global object')
})()

globalNS['Promise'] = Promise
// Expose the polyfill if Promise is undefined or set to a
// non-function value. The latter can be due to a named HTMLElement
// being exposed by browsers for legacy reasons.
// https://github.com/taylorhakes/promise-polyfill/issues/114
})