本文将基于 Promise A+ 规范从零实现一个完整的 Promise。Promise A+链接
序言
Promise 是异步编程的一种解决方案,相比传统的解决方案——回调函数和事件——它更合理和强大。
一个 Promise 的当前状态必须为以下三种状态中的一种:等待态(Pending)、执行态(Fulfilled)和 拒绝态(Rejected),并且状态变化是不可逆的。
用法:
let promise = new Promise((resolve,reject) => {
resolve('success')
})
promise.then(data => { // 成功的回调
console.log(data)
}, err => { // 失败的回调
console.log(err)
})
特点:
- 实例化
Promise会传入一个executor执行器,该函数会立即执行。
new Promise(() => {})
- 执行
executor时会传入两个函数,执行resolve会把当前promise的状态置为Fulfilled,执行reject会把promise的状态置为Rejected,并且状态是不可逆的(不能先成功在失败、先失败在成功等)。
new Promise((resolve, reject) => {
resolve('success')
reject('err') // 不会生效
})
- 返回的实例上会有
then方法,执行then方法时会传入两个回调函数。如果当前promise的状态为Fulfilled,会执行第一个回调函数,函数参数即为调用resolve函数时传入的参数。反之,promise状态为Rejected, 执行第二个回调函数, 函数参数即为调用reject函数时传入的参数。
const promise = new Promise((resolve) => {
resolve('success')
})
promise.then(data => { // data即为调用resolve函数时传入的参数
console.log(data) // 打印 'success'
}, (err) => {
console.log(err)
})
------------------------------------------------------------------------------
const promise2 = new Promise((resolve, reject) => {
reject('error')
})
promise2.then(data => {
console.log(data)
}, (err) => { // err即为调用reject函数时传入的参数
console.log(err) // 打印 'error'
})
- 返回了成功的
promise或一个普通值(包括 undefined),走下一个then的成功,返回了失败的promise或抛出错误,走下一个promise的失败。
const promise = new Promise((resolve, reject) => {
resolve('success')
})
promise.then(data => {
return 'success'
// return new Promise(resolve => resolve('success'))
}).then(data => {
console.log(data) // 打印 'success'
})
------------------------------------------------------------------------------
const promise = new Promise((resolve, reject) => {
reject('error')
// throw new Error('错误了')
})
promise.then(data => {
console.log(data)
}, (err) => {
console.log(err) // 打印 'error'
// 由于没有返回值,相当于 return undefined
}).then(data => {
console.log(data) // 打印 'undefined'
})
Promise.resolve和Promise.reject相比,前者有等待效果。如果Promise.resolve的值还是promise,它会等待这个promise执行完成。 如下实例:
const promise = Promise.resolve(new Promise((resolve) => {
setTimeout(() => {
resolve('success')
}, 1000)
}))
promise.then(data => {
console.log(data)
})
- 有的时候我们会在异步中调用
resolve、reject函数,此时由于状态一直是Pending, 当调用then函数时,既不会调用成功的回调,也不会调用失败的回调。因此我们可以先把用户传入的回调函数收集起来,当调用resolve或reject函数时,让收集的回调函数依次触发。
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success') // 当用户调用resolve 函数时,拿到成功回调数组中的每一项依次执行
}, 3000)
})
promise.then(data => { // 先把(data) => {} 函数放到成功回调的数组中, 把(err) => {} 函数放到失败回调的数组中。
console.log(data)
}, (err) => {
console.log(err)
})
- 实例化
Promise函数后,会返回该对象的实例。该实例上会有then、catch、finally等一些列方法。Promise上的静态方法有all、race。
Promise.all = function(promises) {} // 并发执行(通过计数的方式来实现)
Promise.race = function(promises) {} // 竞赛
let p = new Promise((resolve) => resolve('success'))
p.then(() => {}, () => {})
p.catch(() => {})
p.then().finally(() => {})
核心流程
const PENDING = 'PENDING' // 等待态
const RESOLVED = 'RESOLVED' // 成功态
const REJECTED = 'REJECTED' // 失败态
function resolvePromise (promise2, x, resolve, reject){
// 1) 不能引用同一个对象 可能会造成死循环
if (promise2 === x) {
return reject(new TypeError('Chaining cycle detected for promise #<Promise> --'))
}
let called // 确保不会既调用resolve,又调用reject
if ((typeof x ==='object' && x!=null) || typeof x === 'function') {
try {
let then = x.then
if (typeof then === 'function') { // 认为是promise
// call 改变this指向 并且让函数执行
then.call(x, y => { // y的值可能还是promise,递归解析直至普通值
if (called) return
called = true
resolvePromise(promise2, y, resolve, reject)
}, r => {
if(called) return
called = true
reject(r)
})
} else {
resolve(x)
}
} catch(e) {
if(called) return
called = true
reject(e) // 取值失败
}
} else {
resolve(x)
}
}
class Promise {
constructor(executor) {
this.status = PENDING // 默认是等待态
this.value = undefined // 存储resolve时用户传入的值
this.reason = undefined // 失败reject时用户传入的值
this.onResolvedCallbacks = [] // 存放成功的callback
this.onRejectedCallbacks = [] // 存放失败的callback
let resolve = (value) => {
if (value instanceof Promise) {
value.then(resolve, reject) // resolve的值还是promise,递归解析直至普通值
return
}
if (this.status === PENDING) { // 保证状态不可逆
this.value = value
this.status = RESOLVED
this.onResolvedCallbacks.forEach(fn => fn()) // 发布订阅
}
}
let reject = (reason) => {
if (this.status === PENDING) {
this.reason = reason
this.status = REJECTED
this.onRejectedCallbacks.forEach(fn => fn())
}
}
try {
executor(resolve, reject) // 执行executor 传入resolve和reject
} catch (e) {
reject(e) // 如果内部出错直接将e手动的调用reject方法向外传递
}
}
catch(errCallback) { // catch就是没有成功方法的then函数
return this.then(null, errCallback)
}
then(onfulfilled, onrejected) { // then的参数可选
onfulfilled = typeof onfulfilled == 'function'? onfulfilled : v => v
onrejected = typeof onrejected == 'function' ? onrejected: err => { throw err }
// 为了实现链式调用 必须创建一个新的promise并返回(因为状态不可逆)
let promise2 = new Promise((resolve, reject) => {
if (this.status === RESOLVED) { // 执行成功的回调
setTimeout(() => {
try {
let x = onfulfilled(this.value)
resolvePromise(promise2,x,resolve,reject)
} catch (e) {
reject(e)
}
}, 0)
}
if (this.status === REJECTED) { // 执行失败的回调
setTimeout(() => {
try {
let x = onrejected(this.reason)
resolvePromise(promise2,x,resolve,reject)
} catch (e) {
reject(e)
}
}, 0)
}
if (this.status === PENDING) { // executor有异步逻辑, 比如在setTimeout中调用resolve或reject
this.onResolvedCallbacks.push(() => { // AOP 切片编程
setTimeout(() => {
try {
let x = onfulfilled(this.value)
resolvePromise(promise2, x, resolve, reject)
} catch(e) {
reject(e)
}
}, 0)
})
this.onRejectedCallbacks.push(() => {
setTimeout(() => {
try {
let x = onrejected(this.reason)
resolvePromise(promise2, x, resolve, reject)
} catch(e) {
reject(e)
}
}, 0)
})
}
})
return promise2 // 返回新创建的promise
}
static reject(err) { // 快速创建一个失败的promise
return new Promise((resolve, reject) => {
reject(err)
})
}
}
Promise.resolve
特点: 快速创建一个成功的 promise。接收 promise 做参数时,会等到其执行完成。
Promise.resolve = function (data) {
return new Promise((resolve, reject) => {
resolve(data)
})
}
Promise.reject
特点: 快速创建一个失败的 promise,不会有等待效果。
Promise.reject = function (err) {
return new Promise((resolve, reject) => {
reject(err)
})
}
Promise.all
特点: 都成功才算成功, 有一个失败即为失败。
function isDef (v: any): boolean {
return v !== undefined && v !== null
}
function isPromise (val: any): boolean { // vue2中的判断标准
return (
isDef(val) &&
typeof val.then === 'function' &&
typeof val.catch === 'function'
)
}
Promise.all = function (promises) {
return new Promise((resolve, reject) => {
let arr = []
let idx = 0
let processData = (value, index) => {
arr[index] = value
if(++idx === promises.length){
resolve(arr)
}
}
for(let i = 0 ; i < promises.length; i++){
let currentValue = promises[i]
if (isPromise(currentValue)){
currentValue.then(data => {
processData(data, i)
}, reject)
} else {
processData(currentValue,i);
}
}
})
}
Promise.race
特点: 有一个成功即为成功, 有一个失败即为失败。
Promise.race = function (promises) {
return new Promise((resolve, reject) => {
for(let i = 0 ; i < promises.length; i++){
let currentValue = promises[i]
if (isPromise(currentValue)){
currentValue.then(data => {
resolve(data)
}, reject)
} else {
resolve(currentValue)
}
}
})
}