本文已参与「新人创作礼」活动,一起开启掘金创作之路。
手写Promise
整体架构
Es5的写法
~(function (window) {
const PENDING = 'pending'
const RESOLVED = 'resolved'
const REJECTED = 'rejected'
function Promise(executor) {
this.state = PENDING
this.data = null
this.callbacks = []
function resolve(value) {
// Promise的状态只能改一次,所以只有pending状态时候才能执行resolve和reject
// 当Promise的状态为resolved|rejected就不能再次改变状态了
if (this.state !== PENDING) {
return
}
this.state = RESOLVED
this.data = value
if (this.callbacks.length > 0) {
setTimeout(() => {
this.callbacks.forEach(callbacksObj => {
callbacksObj.onResolved(value)
})
})
}
}
function reject(reason) {
// Promise的状态只能改一次,所以只有pending状态时候才能执行resolve和reject
// 当Promise的状态为resolved|rejected就不能再次改变状态了
if (this.state !== PENDING) {
return
}
this.state = REJECTED
this.data = reason
if (this.callbacks.length > 0) {
setTimeout(() => {
this.callbacks.forEach(callbacksObj => {
callbacksObj.onRejected(reason)
})
})
}
}
try {
executor(resolve.bind(this), reject.bind(this))
} catch (e) {
// 执行器抛出异常,Promise状态为失败,调用reject
reject(e)
}
}
Promise.prototype = {
constructor: Promise,
then(onResolved, onRejected) {
// 指定默认的回调函数
// 如果用户没有指定onRejected | onResolved 我们需要把值|异常穿透下去
// 异常穿透的关键一步,内部会捕捉并返回一个状态失败的Promise
onRejected = typeof onRejected === 'function' ? onRejected : reason => {
throw reason
}
onResolved = typeof onResolved === 'function' ? onResolved : value => value
// 1. then中的回调函数发生异常,then返回失败的Promise,reason为异常
// 2. then的回调函数运行,返回非Promise值,then返回成功的Promise,value为return的值
// 3. then的回调函数运行,返回Promise,则then返回该promise
const self = this
return new Promise((resolve, reject) => {
function handle(callback) {
try {
const result = callback(self.data)
if (result instanceof Promise) {
// 第3种情况,得到返回的promise值|原因,并改变返回的promise状态
result.then(resolve, reject)
} else {
// 第2种情况
resolve(result)
}
} catch (e) {
// 第1种情况
reject(e)
}
}
if (self.state === PENDING) {
// Promise状态为pending,先把回调函数放到队列里
self.callbacks.push({
onResolved() {
handle(onResolved)
},
onRejected() {
handle(onRejected)
}
})
} else if (self.state === RESOLVED) {
// Promise状态为resolved,异步执行回调函数,并通过handle改变return的Promise
setTimeout(() => {
handle(onResolved)
})
} else {
// Promise状态为rejected,异步执行回调函数,并通过handle改变return的Promise
setTimeout(() => {
handle(onRejected)
})
}
})
},
catch (onRejected) {
// 第一个参数写null,内部会把value数据向下传递
return this.then(null, onRejected)
}
}
Promise.resolve = function (value) {
// value 可以是Promise,也可以是值
// Promise => 根据promise状态改变return的状态
// 值 => 成功的Promise
return new Promise((resolve, reject) => {
if (value instanceof Promise) {
value.then(resolve, reject)
} else {
resolve(value)
}
})
}
Promise.reject = function (reason) {
return new Promise((resolve, reject) => {
reject(reason)
})
}
Promise.all = function (iterable) {
return new Promise((resolve, reject) => {
const arr = new Array(iterable.length)
const counter = 0
iterable.forEach((p, index) => {
// 如果iterable中有值不是promise,那么包装成promise
Promise.resolve(p).then(
value => { // 执行onResolved几次,就成功几次
// 这里不能用value代替counter,因为所有的操作都是异步的,不能确定谁先完成,如果最后一个先完成就会出问题
arr[index] = value
counter++
counter === arr.length ? resolve(arr) : null
},
reason => { // 只要有一个失败,整个promise失败
reject(reason)
}
)
})
})
}
Promise.all(iterable) {
return new Promise((resolve, reject) => {
let arr = []
iterable.forEach((p, index) => {
Promise.resolve(p).then(
value => {
arr[index] = value
if(arr.length === iterable.length) {
resolve(arr)
}
} ,
reason => {
reject(reason)
}
)
})
})
}
Promise.race = function (iterable) {
return new Promise((resolve, reject) => {
iterable.forEach(p => {
// 如果iterable中有值不是promise,那么包装成promise
Promise.resolve(p).then(
value => {
// 先成功,先调用,后续的不再改变状态
resolve(value)
},
reason => {
// 先失败,先调用,后续的不再改变状态
reject(reason)
}
)
})
})
}
// 自定义工具方法
Promise.resolveDelay = function(value, time) {
/**
* 返回一个成功的Promise对象,它在指定时间后才能成功
*/
return new Promise((resolve, reject) => {
setTimeout(() => {
if (value instanceof Promise) {
value.then(resolve, reject)
} else {
resolve(value)
}
}, time)
})
}
Promise.rejectDelay = function(reason, time) {
/**
* 返回一个Promise对象,它在指定时间后才能确定失败
*/
return new Promise((resolve, reject) => {
setTimeout(() => {
reject(reason)
}, time)
})
}
window.Promise = Promise
})(window)
Class写法
const PENDING = 'pending'
const RESOLVED = 'resolved'
const REJECTED = 'rejected'
class Promise {
constructor(executor) {
this.state = PENDING
this.data = null;
this.callbacks = []
let resolve = value => {
if(this.state !== PENDING) {
return
}
this.state = RESOLVED
this.data = value
if(this.callbacks.length) {
process.nextTick(() => {
this.callbacks.forEach(item => {
item.onResolved(this.data)
})
})
}
}
let reject = reason => {
if(this.state !== PENDING) {
return
}
this.state = REJECTED
this.data = reason
if(this.callbacks.length) {
process.nextTick(() => {
this.callbacks.forEach(item => {
item.onRejected(this.data)
})
})
}
}
try {
executor(resolve, reject)
} catch(e) {
reject(e)
}
}
then(onResolved, onRejected) {
onResolved = typeof onResolved === 'function' ? onResolved : value => value
onRejected = typeof onRejected === 'function' ? onRejected : reason => {throw reason}
return new Promise((resolve, reject) => {
let handle = callback => {
try {
const result = callback(this.data)
if(result instanceof Promise) {
result.then(resolve, reject)
} else {
resolve(result)
}
} catch(e) {
reject(e)
}
}
if(this.state === PENDING) {
this.callbacks.push({
onResolved() {
handle(onResolved)
},
onRejected() {
handle(onRejected)
}
})
} else if(this.state === RESOLVED) {
process.nextTick(() => {
handle(onResolved)
})
} else {
process.nextTick(() => {
handle(onRejected)
})
}
})
}
catch(onRejected) {
return this.then(null, onRejected)
}
static resolve(value) {
return new Promise((resolve, reject) => {
if(value instanceof Promise) {
value.then(resolve, reject)
} else {
resolve(value)
}
})
}
static reject(reason) {
return new Promise((resolve, reject) => {
if(reason instanceof Promise) {
reason.then(reject, reject)
} else {
reject(reason)
}
})
}
static all(iterable) {
return new Promise((resolve, reject) => {
const arr = []
let counter = 0
iterable.forEach((p, index) => {
Promise.resolve(p).then(
value => {
counter++
arr[index] = value
if(counter === iterable.length) {
resolve(arr)
}
},
reason => {
reject(reason)
}
)
})
})
}
}
module.exports = Promise