分步骤实现 A+ 规范的 Promise
lovelmh13 opened this issue · 0 comments
lovelmh13 commented
1. 初级版
function MyPromise(fn) {
this.status = 'pending'
this.val = ''
this.error = ''
this.fulfilledList = []
this.rejectedList = []
const resolve = (params) => {
// 注意 this ,如果不暂存 this,就要用箭头函数
if (this.status === 'pending') {
this.status = 'fulfilled'
this.val = params
}
}
const reject = (error) => {
if (this.status === 'pending') {
this.status = 'rejected'
this.error = error
}
}
fn(resolve, reject)
}
MyPromise.prototype.then = function (fulfilledFn, rejectedFn) {
if (this.status === 'fulfilled') {
fulfilledFn(this.val)
}
if (this.status === 'rejected') {
rejectedFn(this.error)
}
}
new MyPromise((resolve, reject) => {
resolve(1)
}).then(
(data) => {
console.log(data)
},
(err) => {
console.log(err)
}
)
2. 支持异步版
function MyPromise(fn) {
this.status = 'pending'
this.val = ''
this.error = ''
this.fulfilledList = []
this.rejectedList = []
const resolve = (params) => {
// 注意 this ,如果不暂存 this,就要用箭头函数
if (this.status === 'pending') {
this.status = 'fulfilled'
this.val = params
this.fulfilledList.forEach((fn) => {
fn(this.val)
})
}
}
const reject = (error) => {
if (this.status === 'pending') {
this.status = 'rejected'
this.error = error
this.rejectedList.forEach((fn) => {
fn(this.error)
})
}
}
fn(resolve, reject)
}
MyPromise.prototype.then = function (fulfilledFn, rejectedFn) {
if (this.status === 'fulfilled') {
fulfilledFn(this.val)
}
if (this.status === 'rejected') {
rejectedFn(this.error)
}
if (this.status === 'pending') {
this.fulfilledList.push(fulfilledFn)
this.rejectedList.push(rejectedFn)
}
}
new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve(1)
}, 1000)
}).then(
(data) => {
console.log(data)
},
(err) => {
console.log(err)
}
)
3. 链式调用版
不支持 then 里再返回 Promise 的 版本
function MyPromise(fn) {
this.status = 'pending'
this.val = ''
this.error = ''
this.fulfilledList = []
this.rejectedList = []
const resolve = (params) => {
// 注意 this ,如果不暂存 this,就要用箭头函数
if (this.status === 'pending') {
this.status = 'fulfilled'
this.val = params
this.fulfilledList.forEach((fn) => {
fn(this.val)
})
}
}
const reject = (error) => {
if (this.status === 'pending') {
this.status = 'rejected'
this.error = error
this.rejectedList.forEach((fn) => {
fn(this.error)
})
}
}
fn(resolve, reject)
}
MyPromise.prototype.then = function (fulfilledFn, rejectedFn) {
return new MyPromise((resolve, reject) => {
// 成功的回调
const fulfilledCb = () => {
queueMicrotask(() => {
// 别忘了 then 是微任务, queueMicrotask 在 window 对象上才有
try {
const res = fulfilledFn(this.val)
resolve(res)
} catch (err) {
reject(err)
}
})
}
// 错误的回调
const rejectedCb = () => {
queueMicrotask(() => {
try {
const res = rejectedFn(this.error) // 注意这里,虽然是 rejected 状态,但是回调返回的值,如果不是错误,依然会走 then 的 MyPromise 的
resolve(res)
} catch (error) {
reject(error)
}
})
}
if (this.status === 'fulfilled') {
fulfilledCb()
}
if (this.status === 'rejected') {
rejectedCb()
}
if (this.status === 'pending') {
this.fulfilledList.push(fulfilledCb)
this.rejectedList.push(rejectedCb)
}
})
}
new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve(1)
}, 1000)
})
.then(
(data) => {
console.log(data)
return '第一个完成'
},
(err) => {
console.log(err)
}
)
.then((data) => {
console.log(data)
})
console.log('同步函数')
支持 then 里返回 Promise 的版本
function MyPromise(fn) {
this.status = 'pending'
this.val = ''
this.error = ''
this.fulfilledList = []
this.rejectedList = []
const resolve = (value) => {
if (value instanceof MyPromise) {
return value.then(resolve, reject) // 细节,适用于 then 里又 return Promise.resolve() 的情况
}
// 注意 this ,如果不暂存 this,就要用箭头函数
if (this.status === 'pending') {
this.status = 'fulfilled'
this.val = value
this.fulfilledList.forEach((fn) => {
fn(this.val)
})
}
}
const reject = (error) => {
if (this.status === 'pending') {
this.status = 'rejected'
this.error = error
this.rejectedList.forEach((fn) => {
fn(this.error)
})
}
}
fn(resolve, reject)
}
MyPromise.prototype.then = function (fulfilledFn, rejectedFn) {
const thenPromise = new MyPromise((resolve, reject) => {
// 成功的回调
const fulfilledCb = () => {
queueMicrotask(() => {
// 别忘了 then 是微任务, queueMicrotask 在 window 对象上才有
try {
const res = fulfilledFn(this.val)
resolvePromise(thenPromise, res, resolve, reject)
} catch (err) {
reject(err)
}
})
}
// 错误的回调
const rejectedCb = () => {
queueMicrotask(() => {
try {
const res = rejectedFn(this.error) // 注意这里,虽然是 rejected 状态,但是回调返回的值,如果不是错误,依然会走 then 的 MyPromise 的
resolvePromise(thenPromise, res, resolve, reject)
} catch (error) {
reject(error)
}
})
}
if (this.status === 'fulfilled') {
fulfilledCb()
}
if (this.status === 'rejected') {
rejectedCb()
}
if (this.status === 'pending') {
this.fulfilledList.push(fulfilledCb)
this.rejectedList.push(rejectedCb)
}
})
return thenPromise
}
// 处理当 then 里又 return Promise 的情况
const resolvePromise = (thenPromise, result, resolve, reject) => {
if (thenPromise === result) {
return reject(new TypeError('error due to circular reference')) // 死循环
}
let called // 防止多次调用(这个变量其实不知道是为什么需要的)
if (
result !== null &&
(typeof result === 'object' || typeof result === 'function')
) {
try {
const thenable = result.then // 鸭子辨型,如果有 then 属性,就会被认为是 Promise
if (typeof thenable === 'function') {
thenable.call(
result,
(data) => {
if (called) {
return
}
called = true
resolvePromise(thenPromise, data, resolve, reject) // 返回的可能是一个 Promise 实例或者是一个普通值,所以就递归
},
(error) => {
if (called) {
return
}
called = true
reject(error)
}
)
} else {
resolve(result)
}
} catch (error) {
if (called) {
return
}
called = true // 防止调用失败后又调用成功
reject(error)
}
} else {
// 普通的值, 或者没有
resolve(result)
}
}
// -------------------- test --------------------
// -------------------- 死循环的例子 --------------------
// const promise = new MyPromise((resolve, reject) => {
// setTimeout(() => {
// resolve('lucas')
// }, 2000)
// })
// promise
// .then(
// (onfulfilled = (data) => {
// console.log(data)
// return onfulfilled(data)
// })
// )
// .then((data) => {
// console.log(data)
// })
// -------------------- 需要递归的例子 --------------------
const promise = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('lucas')
}, 1000)
})
promise
.then((data) => {
console.log(data)
return new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve(`${data} next then`)
}, 2000)
}).then((data) => {
return new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve(`${data} next then`)
}, 2000)
})
})
})
.then((data) => {
console.log(data)
})
4. 支持 Promise 穿透的版本
什么是 Promise穿透:
// -------------------- 穿透的例子 --------------------
const promise = new MyPromise((resolve, reject) => {
setTimeout(() => {
resolve('lucas')
}, 2000)
})
promise.then(null).then((data) => {
console.log(data)
})
/**
* 这段代码将会在 2 秒后输出:lucas。这就是 Promise 穿透现象:
* 给 .then() 函数传递非函数值作为其参数时,实际上会被解析成 .then(null),这时候的表现应该是:
* 上一个 promise 对象的结果进行“穿透”,
* 如果在后面链式调用仍存在第二个 .then() 函数时,将会获取被穿透下来的结果。
**/
function MyPromise(fn) {
this.status = 'pending'
this.val = ''
this.error = ''
this.fulfilledList = []
this.rejectedList = []
const resolve = (value) => {
if (value instanceof MyPromise) {
return value.then(resolve, reject) // 细节,适用于 then 里又 return Promise.resolve() 的情况
}
// 注意 this ,如果不暂存 this,就要用箭头函数
if (this.status === 'pending') {
this.status = 'fulfilled'
this.val = value
this.fulfilledList.forEach((fn) => {
fn(this.val)
})
}
}
const reject = (error) => {
if (this.status === 'pending') {
this.status = 'rejected'
this.error = error
this.rejectedList.forEach((fn) => {
fn(this.error)
})
}
}
fn(resolve, reject)
}
MyPromise.prototype.then = function (fulfilledFn, rejectedFn) {
// 解决 fulfilledFn, rejectedFn 没有传值的问题(Promise 穿透),给一个默认值,就可以继续往下执行了
fulfilledFn =
typeof fulfilledFn === 'function' ? fulfilledFn : (data) => data
rejectedFn =
typeof rejectedFn === 'function'
? rejectedFn
: (error) => {
throw error
}
const thenPromise = new MyPromise((resolve, reject) => {
// 成功的回调
const fulfilledCb = () => {
queueMicrotask(() => {
// 别忘了 then 是微任务, queueMicrotask 在 window 对象上才有
try {
const res = fulfilledFn(this.val)
resolvePromise(thenPromise, res, resolve, reject)
} catch (err) {
reject(err)
}
})
}
// 错误的回调
const rejectedCb = () => {
queueMicrotask(() => {
try {
const res = rejectedFn(this.error) // 注意这里,虽然是 rejected 状态,但是回调返回的值,如
resolvePromise(thenPromise, res, resolve, reject)
} catch (error) {
reject(error)
}
})
}
if (this.status === 'fulfilled') {
fulfilledCb()
}
if (this.status === 'rejected') {
rejectedCb()
}
if (this.status === 'pending') {
this.fulfilledList.push(fulfilledCb)
this.rejectedList.push(rejectedCb)
}
})
return thenPromise
}
// 处理当 then 里又 return Promise 的情况
const resolvePromise = (thenPromise, result, resolve, reject) => {
if (thenPromise === result) {
return reject(new TypeError('error due to circular reference')) // 死循环
}
let called // 防止多次调用(这个变量其实不知道是为什么需要的)
if (
result !== null &&
(typeof result === 'object' || typeof result === 'function')
) {
try {
const thenable = result.then // 鸭子辨型,如果有 then 属性,就会被认为是 Promise
if (typeof thenable === 'function') {
thenable.call(
result,
(data) => {
if (called) {
return
}
called = true
resolvePromise(thenPromise, data, resolve, reject) // 返回的可能是一个 Promise 实例或者
},
(error) => {
if (called) {
return
}
called = true
reject(error)
}
)
} else {
resolve(result)
}
} catch (error) {
if (called) {
return
}
called = true // 防止调用失败后又调用成功
reject(error)
}
} else {
// 普通的值, 或者没有
resolve(result)
}
}
用 promises-aplus-tests 测试 A+
注意,如果用这个测试,需要把代码里的 queueMicrotask 改成 setTimeout,因为 queueMicrotask 是在 window 上才有的
先全部安装 promises-aplus-tests
然后在 MyPromise
的文件后面加上:
MyPromise.defer = MyPromise.deferred = function () {
let dfd = {}
dfd.promise = new MyPromise((resolve, reject) => {
dfd.resolve = resolve
dfd.reject = reject
})
return dfd
}
module.exports = MyPromise
最后在命令行执行 promises-aplus-tests ./A+test.js
5. Promise 的各种 API 实现
// -------------------- catch finally 都是在原型上的方法,都是返回一个 then --------------------
MyPromise.prototype.catch = function (cb) {
return this.then(null, cb)
}
MyPromise.prototype.finally = function (cb) {
return this.then(
(value) => {
return MyPromise.resolve(cb()).then(() => value)
},
(error) => {
return MyPromise.resolve(cb()).then(() => {
throw error
})
}
)
}
// -------------------- resolve reject all any race 都是在实例上的方法,都是返回一个 MyPromise 实例 --------------------
MyPromise.resolve = function (val) {
return new MyPromise((resolve) => {
resolve(val)
})
}
MyPromise.reject = function (err) {
return new MyPromise((resolve, reject) => {
reject(err)
})
}
// ------------------- all any race 都是在实例上的方法,都是返回一个 MyPromise 实例,且都在返回的实例执行循环,使用MyPromise.resolve().then() 返回结果 --------------------
MyPromise.all = function (iterable) {
if (!iterable[Symbol.iterator]) {
return new TypeError(`${iterable} is not iterable`)
}
const arr = Array.from(iterable)
const res = []
return new MyPromise((resolve, reject) => {
for (let i = 0; i < arr.length; i++) {
// 保证顺序
const curr = arr[i]
MyPromise.resolve(curr).then(
(val) => {
res[i] = val
if (i === arr.length - 1) {
resolve(res)
}
},
(error) => {
reject(error)
}
)
}
})
}
MyPromise.any = function (iterable) {
if (!iterable[Symbol.iterator]) {
return new TypeError(`${iterable} is not iterable`)
}
const arr = Array.from(iterable)
const res = []
return new MyPromise((resolve, reject) => {
for (let i = 0; i < arr.length; i++) {
const curr = arr[i]
MyPromise.resolve(curr).then(
(val) => {
resolve(val)
},
(error) => {
res[i] = error
if (i === arr.length - 1) {
reject(res)
}
}
)
}
})
}
// 返回第一个已经结束的状态
MyPromise.race = function (iterable) {
if (!iterable[Symbol.iterator]) {
return new TypeError(`${iterable} is not iterable`)
}
const arr = Array.from(iterable)
return new MyPromise((resolve, reject) => {
for (let i = 0; i < arr.length; i++) {
const curr = arr[i]
MyPromise.resolve(curr).then(resolve, reject)
}
})
}
// -------------------- test --------------------
// -------------------- all --------------------
// const p1 = new MyPromise((resolve, reject) => {
// resolve(1)
// })
// const p2 = new MyPromise((resolve, reject) => {
// resolve(2)
// })
// const p3 = '123'
// const p4 = MyPromise.resolve(4)
// MyPromise.all([p1, p2, p3, p4]).then(
// (data) => {
// console.log(data)
// },
// (error) => {
// console.log(error)
// }
// )
// -------------------- any --------------------
// const pErr = new MyPromise((resolve, reject) => {
// reject('总是失败')
// })
// const pSlow = new MyPromise((resolve, reject) => {
// setTimeout(resolve, 500, '最终完成')
// })
// const pFast = new MyPromise((resolve, reject) => {
// setTimeout(resolve, 100, '很快完成')
// })
// MyPromise.any([pErr, pSlow, pFast]).then((value) => {
// console.log(value)
// })
// 期望输出: "很快完成"
// const pErr = new MyPromise((resolve, reject) => {
// reject('总是失败')
// })
// MyPromise.any([pErr]).then(
// (val) => {
// console.log('val: ', val)
// },
// (err) => {
// console.log(err)
// }
// )
// -------------------- race --------------------
var p1 = new MyPromise(function (resolve, reject) {
setTimeout(resolve, 500, 'one')
})
var p2 = new MyPromise(function (resolve, reject) {
setTimeout(reject, 100, 'two')
})
MyPromise.race([p1, p2]).then(
function (value) {
console.log('value: ', value) // "two"
},
function (err) {
console.log('err: ', err)
}
)