本文编写的原因:为什么没有能通过测例的基于ES6(class)实现Promise?!
主要记录面试高频问题,手写一个Promise...
好吧,“完美”标题党了。。。哈哈哈哈哈~
Promise A+ 的ES6实现
直接略过Promise是什么与基本使用,直接记录ES6(class)的Promise实现
const isFunction = fn => typeof fn === "function"
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class FwzPromise {
constructor(executor) {
if (new.target !== FwzPromise) throw TypeError('undefined is not a promise')
if (!isFunction(executor)) throw TypeError(`Promise resolver ${executor} is not a function`)
this.status = PENDING
this.value = undefined
this.reason = undefined
this.onFulfilledArr = []
this.onRejectedArr = []
//PromiseA+ 2.1
const resolve = (value) => {
if (this.status === PENDING) {
this.status = FULFILLED
this.value = value
this.onFulfilledArr.forEach(fn => fn()) //PromiseA+ 2.2.6.1
}
}
const reject = (reason) => {
if (this.status === PENDING) {
this.status = REJECTED
this.reason = reason
this.onRejectedArr.forEach(fn => fn()) //PromiseA+ 2.2.6.2
}
}
try {
executor(resolve, reject)
} catch(e) {
reject(e)
}
}
// https://stackoverflow.com/questions/2130241/pass-correct-this-context-to-settimeout-callback
// https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
// 浏览器中,setTimeout 执行函数的上下文为 window
// es6 : setTimeout(() => this.fn())
// es5 : setTimeout(fn.bind(this))
then(onFulfilled, onRejected) {
//PromiseA+ 2.2.1 / PromiseA+ 2.2.5 / PromiseA+ 2.2.7.3 / PromiseA+ 2.2.7.4
onFulfilled = isFunction(onFulfilled) ? onFulfilled : value => value
onRejected = isFunction(onRejected) ? onRejected : reason => { throw reason }
// 已用箭头函数绑定, 仅留作说明
const self = this
const nextPromise = new FwzPromise((resolve, reject) => {
// 箭头函数无法改变 this指向, 所以
// 在内部this为 声明箭头函数() => { try... }的 this,即执行setTimeout的上下文(this)
// 而执行setTimeout的上下文(this)为声明箭头函数 fulfilledHandler 的this
// 箭头函数 fulfilledHandler 的this绑定为原外层的promise, 即self
const fulfilledHandler = () => {
setTimeout(() => {
try {
//PromiseA+ 2.2.7.1
const x = onFulfilled(this.value)
this.resolvePromise(nextPromise, x, resolve, reject)
} catch (e) {
//PromiseA+ 2.2.7.2
reject(e)
}
})
}
const rejectedHandler = () => {
setTimeout(() => {
try {
const x = onRejected(this.reason)
this.resolvePromise(nextPromise, x, resolve, reject)
} catch(e) {
reject(e)
}
})
}
if (self.status === FULFILLED) {
//PromiseA+ 2.2.2
//PromiseA+ 2.2.4 --- setTimeout
fulfilledHandler()
} else if (this.status === REJECTED) {
//PromiseA+ 2.2.3
rejectedHandler()
} else if (this.status === PENDING) {
this.onFulfilledArr.push(fulfilledHandler)
this.onRejectedArr.push(rejectedHandler)
}
})
return nextPromise
}
// 如果要完全模拟ES6的Promise,需要将resolvePromise设置为函数内函数
resolvePromise(nextPromise, x, resolve, reject) {
//PromiseA+ 2.3.1
if (nextPromise === x) reject(new TypeError('Chaining cycle'))
if (x && typeof x === 'object' || typeof x === 'function') {
let used //PromiseA+2.3.3.3.3 只能调用一次
try {
let then = x.then
if (isFunction(then)) {
//PromiseA+2.3.3
then.call(x, (y) => {
//PromiseA+2.3.3.1
if (used) return
used = true
this.resolvePromise(nextPromise, y, resolve, reject)
}, (r) => {
//PromiseA+2.3.3.2
if (used) return
used = true
reject(r)
})
} else {
//PromiseA+2.3.3.4
if (used) return
used = true
resolve(x)
}
} catch (e) {
//PromiseA+ 2.3.3.2
if (used) return
used = true
reject(e)
}
} else {
//PromiseA+ 2.3.3.4
resolve(x)
}
}
catch(onRejected) {
return this.then(null, onRejected)
}
finally(cb) {
return this.then(
(value) => FwzPromise.resolve(cb()).then(() => value),
(reason) =>
FwzPromise.resolve(cb()).then(() => {
throw reason;
})
);
}
static resolve(value) {
if (value instanceof FwzPromise) return value;
return new FwzPromise((resolve) => resolve(value));
}
static reject(value) {
return new FwzPromise((resolve, reject) => reject(value));
}
static all(arr) {
return new FwzPromise((resolve, reject) => {
const result = [];
let count = arr.length;
for (let [i, p] of arr.entries()) {
// 数组参数如果不是FwzPromise实例,先调用FwzPromise.resolve
FwzPromise.resolve(p).then(
(res) => {
result[i] = res;
// 所有状态都变成fulfilled时返回的FwzPromise状态就变成fulfilled
if (--count === 0) resolve(result);
},
// 有一个被rejected时返回的FwzPromise状态就变成rejected
reject
);
}
});
}
static race(arr) {
return new FwzPromise((resolve, reject) => {
for (let p of arr) {
// 返回第一个settled的Promise
FwzPromise.resolve(p).then(resolve, reject)
}
});
}
static allSettled(arr) {
// allSettled返回的 Promise 为 fulfilled 状态
return new FwzPromise((resolve, reject) => {
const result = []
let count = arr.length
for (let [i, p] of arr.entries()) {
FwzPromise.resolve(p).then(
(value) => {
result[i] = value
if (--count === 0) resolve(result)
},
(reason) => {
result[i] = reason
if (--count === 0) resolve(result)
}
)
}
})
}
}
module.exports = FwzPromise
准确性检验
以promises-aplus-tests的872测例为准
// npm install -g promises-aplus-tests
// promises-aplus-tests promise.js
// 添加测试接口
FwzPromise.defer = FwzPromise.deferred = function () {
let dfd = {};
dfd.promise = new FwzPromise((resolve, reject) => {
dfd.resolve = resolve;
dfd.reject = reject;
});
return dfd;
}
Promise 标准
Promise定义:请查看MDN 或 更详细的说明ECMA-262
Promise A+ :标准, 参考链接的翻译
参考
Promise的源码实现(完美符合Promise/A+规范)