promise-zy
Version:
A simple implementation of Promies/A+
465 lines (456 loc) • 19.3 kB
JavaScript
/*
* @Author: zhaoye
* @Date: 2018-04-17 19:02:17
* @Last Modified by: zhaoye
* @Last Modified time: 2018-04-18 21:20:30
* A simple implementation of Promise/A+
*/
// for test
let dontThrow = false
// specification
// https://promisesaplus.com/#point-56
// 中文:https://segmentfault.com/a/1190000002452115
/**
* 判断一个参数是不是thenable
* @param {any} x
*/
function isThenable (x) {
if (typeof x == 'function' || typeof x == 'object' && x != null) {
// TODO: 2.3.3.1 将 then 赋为 x.then. [3.5]
// 这条没看懂,不知道实现了没
try {
let then = x.then
if (then && typeof then == 'function') {
return {
throwed: false,
then,
}
}
} catch (e) {
return {
throwed: true,
then: e,
}
}
}
return {
throwed: false,
then: null,
}
}
/**
* 报错
* @param {*} reason
*/
function uncaughtError(r) {
return new Error(`Uncaught (in promise) ` + r)
}
/**
* Promise/A+ 实现
*/
class Promise {
/**
* 构造函数
* @param {function} function(resolve, reject) {}
*/
constructor (cb) {
if (typeof cb != 'function') throw new Error('need 1 argument and it should be a function')
this._decideLock = 'locked'
this._retrivers = []
// 私有数据,防止被误用或篡改
let __statusTrigger = false
let __valueTrigger = false
let __PromiseStatus = 'pending'
let __PromiseValue
Object.defineProperties(this, {
_PromiseStatus: {
get () {
return __PromiseStatus
},
set (value) {
// you are doing an unilegal set, please use _decide fn
if (this._decideLock == 'locked') return false
// 2.1
/*
一个Promise必须处在其中之一的状态:pending, fulfilled 或 rejected.
如果是pending状态,则promise:
可以转换到fulfilled或rejected状态。
如果是fulfilled状态,则promise:
不能转换成任何其它状态。
必须有一个值,且这个值不能被改变。
如果是rejected状态,则promise可以:
不能转换成任何其它状态。
必须有一个原因,且这个值不能被改变。
”值不能被改变”指的是其identity不能被改变,而不是指其成员内容不能被改变。
*/
// 2.2.2 如果onFulfilled是一个函数:
/*
它必须在promise fulfilled后调用, 且promise的value为其第一个参数。
它不能在promise fulfilled前调用。
不能被多次调用。
*/
// 2.2.3 如果onRejected是一个函数,
/*
它必须在promise rejected后调用, 且promise的reason为其第一个参数。
它不能在promise rejected前调用。
不能被多次调用。
*/
// the promise status cannot be set again
if (__statusTrigger) {
return false
}
if (__PromiseStatus == 'pending' && value != 'pending') {
__statusTrigger = true
__PromiseStatus = value
if (this._retrivers.length == 0 && this._PromiseStatus == 'rejected') {
if (!dontThrow) {
throw uncaughtError(this._PromiseValue)
}
}
setTimeout(() => {
this._onStatusChange(value)
})
}
},
},
_PromiseValue: {
get () {
return __PromiseValue
},
set (value) {
// you are doing an unilegal set, please use _decide fn
if (this._decideLock == 'locked') return false
// 2.1
if (__valueTrigger) {
// the promise value cannot be set again
return false
}
__valueTrigger = true
__PromiseValue = value
}
}
})
const resolve = (arg) => {
this._decide('fufilled', arg)
}
const reject = (arg) => {
this._decide('rejected', arg)
}
try {
cb(resolve, reject)
} catch (e) {
setTimeout(() => {
this._decide('rejected', e)
});
}
}
/**
* promise的状态和value的变更,必须一起进行
* 所以封装decide方法,并且配合一个锁,防止状态和value被decide方法之外的其他方法修改
* @param {string} status
* @param {*} value
*/
_decide (status, value) {
this._decideLock = 'unlock'
this._PromiseValue = value
this._PromiseStatus = status
this._decideLock = 'locked'
}
/**
* 当promise实例的状态被决定(settled),触发此事件
* @param {string} status
*/
_onStatusChange (status) {
// 释放之前pending状态时的取值器
// 2.2.6 对于一个promise,它的then方法可以调用多次.
// 当promise fulfilled后,所有onFulfilled都必须按照其注册顺序执行。
// 当promise rejected后,所有onRejected都必须按照其注册顺序执行。
while (this._retrivers.length > 0) {
const retriver = this._retrivers.shift()
try {
let x
if (status == 'fufilled') {
if (!retriver.onFufilled) {
// 2.2.7.3 如果 onFulfilled 不是一个函数且promise1已经fulfilled,则promise2必须以promise1的值fulfilled.
x = this._PromiseValue
} else {
// 2.2.7.1 如果onFulfilled 或 onRejected 返回了值x, 则执行Promise 解析流程[[Resolve]](promise2, x).
// 2.2.5 onFulfilled 和 onRejected 必须被当做函数调用 (i.e. 即函数体内的 this 为undefined). [3.2]
x = retriver.onFufilled.call(undefined, this._PromiseValue)
}
} else if (status == 'rejected') {
if (!retriver.onRejected) {
// 2.2.7.4 如果 onRejected 不是一个函数且promise1已经rejected, 则promise2必须以相同的reason被拒绝.
if (this._PromiseValue instanceof Error) {
throw this._PromiseValue
} else {
this._reject(retriver.promise2, this._PromiseValue)
continue
}
} else {
// 2.2.7.1 如果onFulfilled 或 onRejected 返回了值x, 则执行Promise 解析流程[[Resolve]](promise2, x).
// 2.2.5 onFulfilled 和 onRejected 必须被当做函数调用 (i.e. 即函数体内的 this 为undefined). [3.2]
x = retriver.onRejected.call(undefined, this._PromiseValue)
}
}
this._resolve(retriver.promise2, x)
} catch (e) {
// 2.2.7.2 如果onFulfilled 或 onRejected抛出了异常e, 则promise2应当以e为reason被拒绝。
this._reject(retriver.promise2, e)
}
}
}
/**
* 2.3 [[Resolve]](promise, x)
*
* @param {*} promise2 then函数返回的那个promise实例
* @param {*} x 当前的promise实例的执行的结果
*/
_resolve (promise2, x) {
let {throwed, then} = isThenable(x)
if (throwed) {
throw then
}
// 2.3.1 如果promise 和 x 指向相同的值, 使用 TypeError做为原因将promise拒绝
if (promise2 == x) {
this._reject(promise2, new TypeError('as [[Resolve]](promise, x) defined, promise === x is wrong'))
}
// 2.3.2 如果 x 是一个promise, 采用其状态 [3.4]:
if (x instanceof Promise) {
// 2.3.2.1 如果x是pending状态,promise必须保持pending走到x fulfilled或rejected.
if (x._PromiseStatus == 'pending') {
// promise2 wait until x is not pending
// and trigger promise2 status
// 为了通过测试用例,各种倒状态
x.then(function (x) {
let {throwed, then} = isThenable(x)
if (throwed) {
promise2._decide('rejected', then)
} else if (then) {
try {
then.call(x, function (y) {
promise2._decide('fufilled', y)
}, function (r) {
promise2._decide('rejected', r)
})
} catch (e) {
promise2._decide('rejected', e)
}
} else {
promise2._decide('fufilled', x)
}
}, function (r) {
promise2._decide('rejected', r)
})
// 2.3.2.2 如果x是fulfilled状态,将x的值用于fulfill promise
} else if (x._PromiseStatus == 'fufilled') {
// // 递归去解决吧=。=
this._resolve(promise2, x._PromiseValue)
// 2.3.2.3 如果x是rejected状态, 将x的原因用于reject promise
} else if (x._PromiseStatus == 'rejected') {
// 递归去解决吧=。=
if (x._PromiseValue instanceof Promise) {
this._resolve(promise2, x._PromiseValue)
} else {
promise2._decide('rejected', x._PromiseValue)
}
}
// 2.3.3 如果x是一个对象或一个函数
} else if (then) {
// 2.3.3.3 如果 then 是一个函数, 以x为this调用then函数, 且第一个参数是resolvePromise,第二个参数是rejectPromise,且:
// 2.3.3.3.1 当 resolvePromise 被以 y为参数调用, 执行 [[Resolve]](promise, y)
let onceTrigger = false
const resolvePromise = (y) => {
// 2.3.3.3.3 如果 resolvePromise 和 rejectPromise 都被调用了,或者被调用了多次,则只第一次有效,后面的忽略。
if (!onceTrigger) {
onceTrigger = true
// [[Resolve]] (promise, y)
try {
this._resolve(promise2, y)
} catch (e) {
this._reject(promise2, e)
}
}
}
// 2.3.3.3.2 当 rejectPromise 被以 r 为参数调用, 则以r为原因将promise拒绝
const rejectPromise = (r) => {
// 2.3.3.3.3 当 如果 resolvePromise 和 rejectPromise 都被调用了,或者被调用了多次,则只第一次有效,后面的忽略。
if (!onceTrigger) {
onceTrigger = true
this._reject(promise2, r)
}
}
// 2.3.3.3.4 如果在调用then时抛出了异常,则:
try {
then.call(x, resolvePromise, rejectPromise)
} catch (e) {
// 如果 resolvePromise 或 rejectPromise 已经被调用了,则忽略它。
if (!onceTrigger) {
// 否则, 以e为reason将 promise 拒绝。
// 2.3.3.2 如果在取x.then值时抛出了异常,则以这个异常做为原因将promise拒绝。
this._reject(promise2, e)
}
}
// 2.3.3.4 如果 then不是一个函数,则 以x为值fulfill promise
// 2.3.4 如果 x 不是对象也不是函数,则以x为值 fulfill promise
} else {
promise2._decide('fufilled', x)
}
}
/**
*
* @param {*} promise2 then函数返回的那个promise实例
* @param {*} r 当前的promise实例的失败的原因
*/
_reject (promise2, r) {
promise2._decide('rejected', r)
}
/**
* promise实例的取值器
* @param {*} onFufilled 如果promise实例是fufilled状态,执行onFufilled回调
* @param {*} onRejected 如果promise实例是rejected状态,执行onRejected回调
*/
then (onFufilled, onRejected) {
// 2.2.1.1
if (typeof onFufilled != 'function') {
onFufilled = null
}
// 2.2.1.2
if (typeof onRejected != 'function') {
onRejected = null
}
const promise2 = new Promise(function(){})
// 装弹填充取值器
this._retrivers.push({
onFufilled,
onRejected,
promise2,
})
// 2.2.4 onFulfilled 和 onRejected 只允许在 execution context 栈仅包含平台代码时运行. [3.1].
setTimeout (() => {
// 如果已经不是pending的状态了,立即执行取值器的逻辑
if (this._PromiseStatus != 'pending') {
this._onStatusChange(this._PromiseStatus)
}
})
return promise2
}
/**
* promise实例的取值器,只取失败的值,忽略成功的值
* @param {*} onRejected 如果promise实例是rejected状态,执行onRejected回调
*/
catch (onRejected) {
return this.then(null, onRejected)
}
}
// https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
// Promise.all(iterable) 方法返回一个 Promise 实例
// 此实例在 iterable 参数内所有的 promise 都“完成(resolved)”或参数中不包含 promise 时回调完成(resolve);
// 如果参数中 promise 有一个失败(rejected),此实例回调失败(rejecte),失败原因的是第一个失败 promise 的结果。
// in this implementation, it only supports array map set
// *fully tested whit the suites in MDN demo
Promise.all = function (iterable = []) {
if (!iterable) throw new Error('no argument')
if (iterable instanceof Array || iterable instanceof Map || iterable instanceof Set) {} else throw new TypeError('arg type wrong')
return new Promise(function (resolve, reject) {
const results = []
let failed = false
let failReason
if (iterable.length == 0) resolve(iterable)
iterable.forEach(function (item, index) {
Promise.resolve(item).then(function (value) {
if (failed) return
results.push({
index,
value,
})
if (results.length == iterable.length) {
resolve(results.sort(function(a, b){
return a.index > b.index
}).reduce(function (prev, cur) {
prev.push(cur.value)
return prev
}, []))
}
}, function (reason) {
// 防止重复发生
if (!failed) {
failed = true
reject(reason)
}
})
})
})
}
// https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise/race
// Promise.race(iterable) 方法返回一个 promise
// 并伴随着 promise对象解决的返回值或拒绝的错误原因, 只要 iterable 中有一个 promise 对象"解决(resolve)"或"拒绝(reject)"。
// in this implementation, it only supports array map set
// *fully tested whit the suites in MDN demo
Promise.race = function (iterable) {
if (!iterable) throw new Error('no argument')
if (iterable instanceof Array || iterable instanceof Map || iterable instanceof Set) {} else throw new TypeError('arg type wrong')
return new Promise(function (resolve, reject) {
let winner
if (iterable.length == 0) resolve(iterable)
iterable.forEach(function (item, index) {
Promise.resolve(item).then(function (value) {
if (!winner) {
winner = value
resolve(winner)
}
}, function (reason) {
if (!winner) {
winner = reason
reject(winner)
}
})
})
})
}
// https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise/resolve
// Promise.resolve(value)方法返回一个以给定值解析后的Promise对象。
// 但如果这个值是个thenable(即带有then方法),返回的promise会“跟随”这个thenable的对象,
// 采用它的最终状态(指resolved/rejected/pending/settled);如果传入的value本身就是promise对象,
// 则该对象作为Promise.resolve方法的返回值返回;否则以该值为成功状态返回promise对象。
// *fully tested whit the suites in MDN demo
Promise.resolve = function (x) {
let {throwed, then} = isThenable(x)
if (throwed) {
return new Promise(function (resolve, reject) {
reject(then)
})
}
if (x instanceof Promise) {
return x
} else if (then) {
return new Promise(function (resolve, reject) {
try {
then.call(x, function (y) {
resolve(y)
}, function (r) {
reject(r)
})
} catch (e) {
reject(e)
}
})
} else {
return new Promise(function (resolve, reject) {
resolve(x)
})
}
}
// https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise/reject
// Promise.reject(reason)方法返回一个用reason拒绝的Promise。
// *fully tested whit the suites in MDN demo
Promise.reject = function (reason) {
return new Promise(function (resolve, reject) {
reject(reason)
})
}
// for test
Promise.dontThrow = function () {
dontThrow = true
}
module.exports = Promise