手写Promise对象
最近准备总结一下ES6的知识,今天来手写一个Promise,在写之前我们先来看看Promise的概念、特点及其用法。
概念
Promise是异步编程的一种解决方案。用来解决之前的回调地狱等的问题。
相对于之前的回调函数和事件更合理也更强大
特点
- 对象不受外界影响
- 一旦状态状态改变,就不会在变。
缺点
- 无法取消,一旦创建就会立即执行
- 不设置回调函数,抛出的错误不会反应到外部
- 当处于Pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
基本用法
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
实现一个Promise
Promise的方法
实例方法:
- then
- catch
- finally
静态方法:
- resolve
- reject
- race
- all
- allSettled
- any
接下来我们根据Promise的定义及它的特点来实现一个Promise。
构造函数
首先,Promise是一个构造函数,所以我们要定一个类,添加构造函数,定义resolve reject 接收参数。
// 1. Promise是一个构造函数,所以我们要定一个class类
class Promise {
// 2. 添加构造函数,通过`new`命令生成对象实例时,自动调用该方法
constructor (func){
// 3. 定义`resolve` `reject` 接收参数
const resolve = ( ) => {}
const reject = () => {}
// 4.执行回调函数,因为Promise一旦创建立即执行
func(resolve, reject)
}
}
状态及原因
Promise是根据状态机制来实现的,有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”,表示其他手段无法改变。
const PENDING = 'pending' // 进行中
const FULFILLED = 'fulfilled' // 已成功
const REJECTED = 'rejected' // 以失败
class Promise {
// 1. 添加状态 (pending / fulfilled / rejected) 初始是pending状态
state = PENDING
// 2. 添加原因
result = undefined
constructor (func){
// 3. 调整resolve reject
const resolve = (result) => {
// 4. 状态不可变
if(this.setate === PENDING){
this.state = FULFILLED
this.result = result
}
}
const reject = (result) => {
if(this.setate === PENDING){
this.state = REJECTED
this.result = result
}
}
func(resolve, reject)
}
}
then方法
Promise 实例的 then() 方法最多接受两个参数:用于 Promise 兑现和拒绝情况的回调函数。它立即返回一个等效的 Promise 对象,允许你链接到其他 Promise 方法,从而实现链式调用。
然后,我们来看一下MDN对参数的解释:
根据then方法的定义和对参数的描述,来总结一下then需要实现的几点:
- 参数需要进行判断:
onFulfilled如果不是函数就将其包装成一个函数((x) => x)。onRejected如果不是一个函数就将其包装成一个抛出错误的函数((x) => { throw x;}) then方法会返回一个等效的Promsie对象,来实现链式调用,所以需要抛出一个Promise函数。then方法的回调函数是异步执行的。参考vue2中的写法,因为我们是手写Promise所以排除Promise,运行环境是浏览器排除process.nextTick,所以选用queueMicrotask,MutationObserver、setTimeout。
所以我们需要写一个将回调函数包成成异步代码的方法runAsynctask。
function runAsynctask(callback) {
// 调用核心api(queueMicrotask MutationObserver setTimeout)
if (typeof queueMicrotask === 'function') {
queueMicrotask(callback)
} else if (typeof MutationObserver === 'funciton') {
const obs = new MutationObserver(callback)
const div = document.createElement('div')
obs.observe(div, { childList: true })
div.innerHTML = 'test'
} else {
setTimeout(callback, 0)
}
}
接下来我们写一个初步的then方法
then(onFulfilled, onRejected) {
// 1. 参数需要进行判断
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : x => x
onRejected = typeof onRejected === 'function' ? onRejected : x => { throw x }
// 2. 返回一个Promise(这里的Promise指的是我们class创建的Promise)
const p2 = new Promise((resolve, reject) => {
if (this.state === FULFILLED) {
// 3. then的回调是异步执行的,直接调用上面写的runAsynctask方法
runAsynctask(() => {
// 调用成功回调reslove
reslove(onFulfilled(this.result))
})
} else if (this.state === REJECTED) {
runAsynctask(() => {
// 调用失败回调reslove
reject(onRejected(this.result))
})
} else if (this.state === PENDING) {
// 执行then时,状态还没有改变,则把回调存起来,等执行回调的时候再一次执行。
this.#handlers.push({
onFulfilled: () => {
runAsynctask(() => {
onFulfilled(this.result)
})
},
onRejected: () => {
runAsynctask(() => {
onRejected(this.result)
})
}
})
}
})
return p2
}
接下来我们看一下Promise.then()针对执行函数返回值是如何处理的。主要有以下几种情况:
- 重复引用,会报一个错误(
chaining cycle deteted for promise #<Promise>)如下图:
- 如果
Promise的回调函数中返回的是Promise,那么无论触发的是Promise链中的then方法还是catch方法,新生成的Promise对象的状态都直接取决于回调函数中返回的Promise对象的状态,传进下一个回调函数的值也取决于这个被返回的Promise对象,让我们看下面几个例子:
- 对象或者函数
- 返回值
把这些情况封装成一个函数就是:
function resolvePromise(p2, x, resolve, reject) {
// 1. 处理重复引用
if (x === p2) {
throw new TypeError('chaining cycle deteted for promise #<Promise>')
}
// 2. 处理返回一个Promise
if (x instanceof Promise) {
x.then((res) => {
resolve(res)
}, (err) => {
reject(err)
})
} else if (x != null && (typeof x === 'object' || typeof x === 'function')) {
// 3. 对象或者函数
try {
var then = x.then;
} catch (err) {
return reject(err)
}
if(typeof then === 'function') {
let called = false
try {
then.call(x,y => {
if(called) return;
called = true;
resolvePromise(p2, y ,resolve,reject)
},r => {
if(called) return;
called = true;
reject(r)
})
} catch (error) {
if(called) return;
called = true;
reject(error)
}
} else {
resolve(x)
}
} else {
// 4. 处理返回值
return resolve(x)
}
}
将上面合并一下:
const PENDING = 'pending' // 进行中
const FULFILLED = 'fulfilled' // 已成功
const REJECTED = 'rejected' // 以失败
class Promise {
// 添加状态 (pending / fulfilled / rejected)
state = PENDING
// 添加原因
result = undefined
// 回调函数的队列
#handlers = []
constructor (func){
// 调整resolve reject
const resolve = (result) => {
// 状态不可变
if(this.setate === PENDING){
this.state = FULFILLED
this.result = result
// 如果在调用resolve时,回调函数里有数据,则循环执行
this.#handlers.forEach(({ onFulfilled }) => {
onFulfilled(result)
})
}
}
const reject = (result) => {
if(this.setate === PENDING){
this.state = REJECTED
this.result = result
// 如果在调用reject时,回调函数里有数据,则循环执行
this.#handlers.forEach(({ onRejected }) => {
onRejected(result)
})
}
}
func(resolve, reject)
}
}
// 1. 添加实例方法
then(onFulfilled, onRejected) {
// 判断参数是否是函数
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : x => x
onRejected = typeof onRejected === 'function' ? onRejected : x => { throw x }
const p2 = new Promise((resolve, reject) => {
if (this.state === FULFILLED) {
runAsynctask(() => {
// 获取返回值
try {
const x = onFulfilled(this.result)
// 处理重复引用
// if(x === p2){
// throw new TypeError('chaining cycle deteted for promise #<Promise>')
// }
// // 判断是否是promise
// if(x instanceof Promise){
// x.then((res) => {
// resolve(res)
// },(err) => {
// reject(err)
// })
// } else {
// resolve(x)
// }
// 调用函数
resolvePromise(p2, x, resolve, reject)
} catch (error) {
reject(error)
}
})
} else if (this.state === REJECTED) {
runAsynctask(() => {
try {
// 获取返回值
const x = onRejected(this.result)
// 处理promise
resolvePromise(p2, x, resolve, reject)
} catch (error) {
reject(error)
}
})
} else if (this.state === PENDING) {
// 执行then时,状态还没有改变,则把回调存起来
this.#handlers.push({
onFulfilled: () => {
runAsynctask(() => {
// 1. 处理异常
try {
// 2. 获取返回值
const x = onFulfilled(this.result)
// 3. 调用函数
resolvePromise(p2, x, resolve, reject)
} catch (error) {
reject(error)
}
})
},
onRejected: () => {
runAsynctask(() => {
try {
const x = onRejected(this.result)
resolvePromise(p2, x, resolve, reject)
} catch (error) {
reject(error)
}
})
}
})
}
})
return p2
}
}
catch实例方法
Promise 实例的 catch() 方法用于注册一个在 promise 被拒绝时调用的函数。它会立即返回一个等效的 Promise 对象,这可以允许你链式调用其他 promise 的方法。此方法是 Promise.prototype.then(undefined, onRejected) 的一种简写形式。
综上所述 catch() 方法如下:
catch(onRejected){
// 内部调用then
return this.then(undefined,onRejected)
}
finally实例方法
finally不管Promise对象最后的状态如何,都会执行的操作。finally本质上是then方法的特例。
那就是then方法不管成功还是失败都执行finally传入的回调函数就可以了,如下:
finally(onFinally){
// 内部调用then
return this.then(onFinally,onFinally)
}
reslove静态方法
Promsie.reslove()静态方法将给定的值转换为一个特定的Promise,如果该值本身是一个Promise,那么该Promise被返回,如果该值是一个thenable对象,Promise.resolve() 将调用其 then() 方法及其两个回调函数;否则,返回的 Promise 将会以该值兑现。
static reslove(value){
if (value instanceof Promise) {
return value
} else {
return new Promise((resolve) => {
resolve(value)
})
}
}
reject静态方法
方法也会返回一个新的 Promise 实例,该实例的状态为rejected
static reject(value){
return new Promise((resolve,reject) => {
reject(value)
})
}
race静态方法
将多个Promise实例,包装成一个新的Promise实例,这个返回的 promise 会随着第一个 promise 的敲定而敲定。
static race(promises) {
// 1. 返回promise
return new Promise((resolve, reject) => {
// 2. 判断是否为数组,错误信息 Argument is not iterable
if (!Array.isArray(promises)) {
return reject(new TypeError('Argument is not iterable'))
}
// 3. 等待第一个敲定
promises.forEach(p => {
this.resolve(p).then((res) => {
resolve(res)
}, (err) => {
reject(err)
})
})
})
}
all静态方法
返回一个Promise,等待所有输入的Promise都兑现,并返回一个包含所有兑现值的数组。如果输入的任何 Promise 被拒绝,则返回的 Promise 将被拒绝,并带有第一个被拒绝的原因。
static all(promises) {
// 返回一个Promise
return new Promise((resolve, reject) => {
// 判断是否为数组
if (!Array.isArray(promises)) {
return reject(new TypeError('Argument is not iterable'))
}
// 如果是空数组,则返回一个兑现的空数组
promises.length === 0 && resolve(promises)
const result = []
let count = 0
promises.forEach((p, index) => {
this.resolve(p).then((res) => {
// 根据下标来保持输出的结果与传入的参数一一对应
result[index] = res
count++ // 用来判断是否是全部兑现
count === promises.length && resolve(result)
}, (err) => {
// 如果其中一个被拒绝,则直接返回别拒绝的原因
reject(err)
})
})
})
}
allSettled静态犯法
Promise.allSettled() 静态方法将一个 Promise 可迭代对象作为输入,并返回一个单独的 Promise。当所有输入的 Promise 都已敲定时(包括传入空的可迭代对象时),返回的 Promise 将被兑现,并带有描述每个 Promise 结果的对象数组。
static allSettled(promises){
// 1. 返回一个promsie
return new Promise((resolve, reject) => {
// 2. 数组判断
if (!Array.isArray(promises)) {
return reject(new TypeError('Argument is not iterable'))
}
// 3. 为空直接敲定
promises.length === 0 && resolve(promises)
// 4. 等待全部敲定
// 4.1 记录结果:数组
const result = []
let count = 0 // 使用次数来记录是否全部敲定
promises.forEach((p,index) => {
this.resolve(p).then((res) => {
// 4.2 处理兑现:{state: 'fulfilled',value:1}
result[index] = {state: FULFILLED, value:res}// 为保持返回数据和传入顺序一致,使用下标赋值
count++
count === promises.length && resolve(result)
},err=>{
// 4.2 处理拒绝
result[index] = { state: REJECTED, reason: err}
count++
count === promises.length && resolve(result)
})
})
})
}
any静态方法
Promise.any() 静态方法将一个 Promise 可迭代对象作为输入,并返回一个 Promise。当输入的任何一个 Promise 兑现时,这个返回的 Promise 将会兑现,并返回第一个兑现的值。当所有输入 Promise 都被拒绝(包括传递了空的可迭代对象)时,它会以一个包含拒绝原因数组的 AggregateError 拒绝。
static any (promises){
// 1. 返回promises,数组判断 错误信息Argument is not iterable
return new Promise((resolve,reject) => {
if(!Array.isArray(promises)){
return reject(new TypeError('Argument is not iterable'))
}
// 2. 空数组 直接拒绝
promises.length === 0 && reject(new AggregateError(promises,'All promises were rejected'))
// 3. 等待结果
const errors = []
let count = 0
promises.forEach((p,index) => {
this.resolve(p).then((res) => {
// 3.1 第一个兑现
resolve(res)
},err => {
// 3.2 全部拒绝 :All promises were rejected --[1,2,3]
errors[index] = err
count++
promises.length === count && reject(new AggregateError(errors,'All promises were rejected'))
})
})
})
}
完整代码
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
// 1. 定义类
class Promise {
// 实例属性
state = PENDING //状态
// 原因
result = undefined
// 回调函数
#handlers = []
// 添加构造函数 new时会执行
constructor(func) {
// 2. 接收回调函数func
// 3. 定义resolve reject 接收参数
const resolve = (result) => {
// pending => fulfilled
// 记录原因
if (this.state === PENDING) {
this.state = FULFILLED
this.result = result
// 如果在调用resolve时,回调函数里有数据,则循环执行
this.#handlers.forEach(({ onFulfilled }) => {
onFulfilled(result)
})
}
}
const reject = (result) => {
// pending => rejected
// 记录原因
if (this.state === PENDING) {
this.state = REJECTED
this.result = result
// 如果在调用reject时,回调函数里有数据,则循环执行
this.#handlers.forEach(({ onRejected }) => {
onRejected(result)
})
}
}
try {
// 4. 执行回调函数,因为Promise一旦创建就会立即执行
func(resolve, reject)
} catch (error) {
reject(error)
}
}
// 添加实例方法
then(onFulfilled, onRejected) {
// 判断参数是否是函数
// 参考 https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise/then
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : x => x
onRejected = typeof onRejected === 'function' ? onRejected : x => { throw x }
const p2 = new Promise((resolve, reject) => {
if (this.state === FULFILLED) {
runAsynctask(() => {
// 获取返回值
try {
const x = onFulfilled(this.result)
// 处理重复引用
// if(x === p2){
// throw new TypeError('chaining cycle deteted for promise #<Promise>')
// }
// // 判断是否是promise
// if(x instanceof Promise){
// x.then((res) => {
// resolve(res)
// },(err) => {
// reject(err)
// })
// } else {
// resolve(x)
// }
// 调用函数
resolvePromise(p2, x, resolve, reject)
} catch (error) {
reject(error)
}
})
} else if (this.state === REJECTED) {
runAsynctask(() => {
try {
// 获取返回值
const x = onRejected(this.result)
// 处理promise
resolvePromise(p2, x, resolve, reject)
} catch (error) {
reject(error)
}
})
} else if (this.state === PENDING) {
// 执行then时,状态还没有改变,则把回调存起来
this.#handlers.push({
onFulfilled: () => {
runAsynctask(() => {
// 1. 处理异常
try {
// 2. 获取返回值
const x = onFulfilled(this.result)
// 3. 调用函数
resolvePromise(p2, x, resolve, reject)
} catch (error) {
reject(error)
}
})
},
onRejected: () => {
runAsynctask(() => {
try {
const x = onRejected(this.result)
resolvePromise(p2, x, resolve, reject)
} catch (error) {
reject(error)
}
})
}
})
}
})
return p2
}
// catch 是.then(null,rejection) 或者.then(undefined,rejection)的别名,用于指定发生错误时的回调函数。
/**
* 用法:
* p.then(res => console.log(res)).catch(err => console.log(err))
*/
/**
* 1.内部调用then
* 2.处理异常
*/
catch(onRejected) {
// 内部调用then
return this.then(undefined, onRejected)
// this.then()
}
/**
* @name finally
* @description 不管Promise对象最后的状态如何,都会执行的操作。finally本质上是then方法的特例
*
*/
finally(onFinally) {
return this.then(onFinally, onFinally)
}
/**
* 是Promise直接返回
* 转为Promise并返回(fulfilled状态)
*/
static resolve(value) {
if (value instanceof Promise) {
return value
} else {
return new Promise((resolve) => {
resolve(value)
})
}
}
/**
* 方法也会返回一个新的 Promise 实例,该实例的状态为rejected
*/
static reject(value) {
return new Promise((resolve, reject) => {
reject(value)
})
}
/**
* @name race 将多个Promise实例,包装成一个新的Promise实例,这个返回的 promise 会随着第一个 promise 的敲定而敲定
* @param {*} array
*
* 1. 返回promise
* 2. 判断是否为数组,错误信息 Argument is not iterable
* 3. 等待第一个敲定
*/
static race(promises) {
// 1. 返回promise
return new Promise((resolve, reject) => {
// 2. 判断是否为数组,错误信息 Argument is not iterable
if (!Array.isArray(promises)) {
return reject(new TypeError('Argument is not iterable'))
}
// 3. 等待第一个敲定
promises.forEach(p => {
//
this.resolve(p).then((res) => {
resolve(res)
}, (err) => {
reject(err)
})
})
})
}
/**
*
* @param {*} promises
* @returns
*
* 1. 返回一个promise
* 2. 判断是否为数组,错误信息 Argument is not iterable
* 3. 是否是空数组,是返回空数组
* 4. 处理全部兑现
* 4.1 记录结果:索引来记录结果,保证结果的顺序和Promise数组的顺序一致
* 4.2 判断全部兑现: 通过兑现的次数进行判断,保证可以获取所有的结果
* 5. 处理第一个拒绝
*/
static all(promises) {
return new Promise((resolve, reject) => {
if (!Array.isArray(promises)) {
return reject(new TypeError('Argument is not iterable'))
}
promises.length === 0 && resolve(promises)
const result = []
let count = 0
promises.forEach((p, index) => {
this.resolve(p).then((res) => {
result[index] = res
count++
count === promises.length && resolve(result)
}, (err) => {
reject(err)
})
})
})
}
/**
* Promise.allSettled
* 1. 传入Promise都变成已敲定,既可获取兑现结果
* 2. 数组判断,错误信息
* 3. 空数组,直接兑现
* 4. 等待全部敲定
* 4.1 记录结果
* 4.2 处理兑现:{state: 'fulfilled',value:1}
* 4.3 处理拒绝:{state: 'rejected',reason:3}
*/
static allSettled(promises){
// 1. 返回一个promsie
return new Promise((resolve, reject) => {
// 2. 数组判断
if (!Array.isArray(promises)) {
return reject(new TypeError('Argument is not iterable'))
}
// 3. 为空直接敲定
promises.length === 0 && resolve(promises)
// 4. 等待全部敲定
// 4.1 记录结果:数组
const result = []
let count = 0 // 使用次数来记录是否全部敲定
promises.forEach((p,index) => {
this.resolve(p).then((res) => {
// 4.2 处理兑现:{state: 'fulfilled',value:1}
result[index] = {state: FULFILLED, value:res}// 为保持返回数据和传入顺序一致,使用下标赋值
count++
count === promises.length && resolve(result)
},err=>{
// 4.2 处理拒绝
result[index] = { state: REJECTED, reason: err}
count++
count === promises.length && resolve(result)
})
})
})
}
/**
* 静态方法 - any
* 1. 返回promises,数组判断 错误信息Argument is not iterable
* 2. 空数组 直接拒绝 拒绝原因:All promises were rejected[]
* 3. 等待结果
* 3.1 第一个兑现
* 3.2 全部拒绝 :All promises were rejected --[1,2,3]
*/
static any (promises){
// 1. 返回promises,数组判断 错误信息Argument is not iterable
return new Promise((resolve,reject) => {
if(!Array.isArray(promises)){
return reject(new TypeError('Argument is not iterable'))
}
// 2. 空数组 直接拒绝
promises.length === 0 && reject(new AggregateError(promises,'All promises were rejected'))
// 3. 等待结果
const errors = []
let count = 0
promises.forEach((p,index) => {
this.resolve(p).then((res) => {
// 3.1 第一个兑现
resolve(res)
},err => {
// 3.2 全部拒绝 :All promises were rejected --[1,2,3]
errors[index] = err
count++
promises.length === count && reject(new AggregateError(errors,'All promises were rejected'))
})
})
})
}
}
// 处理 promise 重复调用问题
function resolvePromise(p2, x, resolve, reject) {
if (x === p2) {
throw new TypeError('chaining cycle deteted for promise #<Promise>')
}
// 判断是否是promise
if (x instanceof Promise) {
x.then((res) => {
resolve(res)
}, (err) => {
reject(err)
})
} else if (x != null && (typeof x === 'object' || typeof x === 'function')) {
try {
var then = x.then;
} catch (err) {
return reject(err)
}
if(typeof then === 'function') {
let called = false
try {
then.call(x,y => {
if(called) return;
called = true;
resolvePromise(p2, y ,resolve,reject)
},r => {
if(called) return;
called = true;
reject(r)
})
} catch (error) {
if(called) return;
called = true;
reject(error)
}
} else {
resolve(x)
}
} else {
return resolve(x)
}
}
// 定义函数
function runAsynctask(callback) {
// 2. 调用核心api(queueMicrotask MutationObserver setTimeout)
if (typeof queueMicrotask === 'function') {
queueMicrotask(callback)
} else if (typeof MutationObserver === 'funciton') {
const obs = new MutationObserver(callback)
const div = document.createElement('div')
obs.observe(div, { childList: true })
div.innerHTML = 'test'
} else {
setTimeout(callback, 0)
}
}