目录
添加 resolve、reject、race、all 等方法
什么是 Promise
Promise 是一种异步编程的解决方案。在异步操作中,callback 会导致回调地狱的问题,Promise 解决了这个问题。
一个 Promise对象有以下三种状态:
- pending:初始状态,既不是成功,也不是失败状态。
- fulfilled(resolved):意味着操作成功完成。
- rejected:意味着操作失败。
Promise对象内部运行的一个变化, 变化如下:
- 当 new Promise() 被实例化后,即表示 Promise 进入 pending 初始化状态,准备就绪,等待运行。
- 一旦 Promise 实例运行成功或者失败之后,实例状态就会变为 fulfilled 或者 rejected,此时状态就无法变更。
在使用 Promise 时,通常会调用其 then() 方法来处理异步操作的结果,或者调用 catch() 方法来处理出错信息。同时,Promise 还提供了一些静态方法,如 Promise.resolve()、Promise.reject() 等用于快速创建一个 Promise 实例。
实现一个 Promise
(下文所指的规定是指 Promise A+规范)
Promise 的声明
首先呢,promise 肯定是一个类,我们就用 class 来声明。
- 由于 new Promise((resolve, reject)=>{}) ,所以传入一个参数(函数 executor),传入就执行
- executor 里面有两个参数,一个叫 resolve(成功),一个叫 reject(失败)
- 由于 resolve 和 reject 可执行,所以都是函数,我们用 let 声明
class myPromise{
// 构造器
constructor(executor){
// 成功
let resolve = () => { };
// 失败
let reject = () => { };
// 立即执行
executor(resolve, reject);
}
}
解决基本状态
Promise 有规定:
- Promise 存在三个状态(state)pending、fulfilled、rejected
- pending(等待态)为初始态,并可以转化为 fulfilled(成功态)和 rejected(失败态)
- 成功时,不可转为其他状态,且必须有一个不可改变的值(value)
- 失败时,不可转为其他状态,且必须有一个不可改变的原因(reason)
- new Promise((resolve, reject)=>{resolve(value)}) resolve 为成功,接收参数 value,状态改变为 fulfilled,不可再次改变。
- new Promise((resolve, reject)=>{reject(reason)}) reject 为失败,接收参数 reason,状态改变为 rejected,不可再次改变。
- 若是 executor 函数报错 直接执行 reject();
于是乎,我们获得以下代码
class myPromise {
constructor(executor) {
// 初始化 state 为等待态
this.state = 'pending'
// 成功的值
this.value = undefined
// 失败的原因
this.reason = undefined
let resolve = value => {
// state 改变 resolve 调用就会失败
if (this.state === 'pending') {
// resolve 调用后 state 转化为成功态
this.state = 'fulfilled'
}
// 储存成功的值
this.value = value
}
let reject = reason => {
// state 改变 reject 调用就会失败
if (this.state === 'pending') {
// reject 调用后 state 转化为失败态
this.state = 'rejected'
}
// 储存失败的原因
this.reason = reason
}
// 立即执行 executor,如果 executor 执行报错,直接执行 reject
try {
executor(resolve, reject)
} catch(err) {
reject(err)
}
}
}
添加 then 方法
Promise 有一个叫做 then 的方法,里面有两个参数:onFulfilled 和 onRejected,成功有成功的值,失败有失败的原因
- 当状态 state 为 fulfilled,则执行 onFulfilled,传入 this.value。
- 当状态 state 为 rejected,则执行 onRejected,传入 this.reason
- onFulfilled 和 onRejected 如果是函数,则必须分别在 fulfilled,rejected 后被调用,value 或reason 依次作为他们的第一个参数
class myPromise {
constructor(executor){...}
// then 方法有两个参数 onFulfilled 和 onRejected
then(onFulfilled, onRejected) {
// 状态为 fulfilled,执行 onFulfilled,传入成功的值
if (this.state === 'fulfilled') {
onFulfilled(this.value)
}
// 状态为 rejected,执行 onRejected,传入失败的原因
if (this.state === 'rejected') {
onRejected(this.reason)
}
}
}
解决异步实现
现在基本可以实现简单的同步代码,但是当 resolve 在 setTomeout 内执行,调用 then 时 state 还是 pending 等待态,我们就需要在 then 调用的时候,将成功和失败存到各自的数组,一旦 reject 或者 resolve,就调用它们
类似于发布订阅,先将 then 里面的两个函数储存起来,由于一个 promise 可以有多个 then,所以存在同一个数组内。
// 多个 then 的情况
let p = new Promise();
p.then();
p.then();
成功或者失败时,forEach 调用它们
class myPromise {
constructor(executor) {
this.state = 'pending'
this.value = undefined
this.reason = undefined
// 成功存放的数组
this.onResolvedCallbacks = []
// 失败存放的数组
this.onRejectedCallbacks = []
let resolve = value => {
if (this.state === 'pending') {
this.state = 'fulfilled'
}
this.value = value
// 一旦 resolve 执行,调用成功数组的函数
this.onResolvedCallbacks.forEach(fn => fn());
}
let reject = reason => {
if (this.state === 'pending') {
this.state = 'rejected'
}
this.reason = reason
// 一旦 reject 执行,调用失败数组的函数
this.onRejectedCallbacks.forEach(fn => fn())
}
try {
executor(resolve, reject)
} catch(err) {
reject(err)
}
}
then(onFulfilled, onRejected) {
if (this.state === 'fulfilled') {
onFulfilled(this.value)
}
if (this.state === 'rejected') {
onRejected(this.reason)
}
// 状态为 pending 时
if (this.state === 'pending') {
// onFulfilled 传入到成功数组
this.onResolvedCallbacks.push(() => onFulfilled(this.value))
// onRejected 传入到失败数组
this.onRejectedCallbacks.push(() => onRejected(this.reason))
}
}
}
解决链式调用
我门常常用到 new Promise().then().then(),这就是链式调用,用来解决回调地狱
- 为了达成链式,我们默认在第一个 then 里返回一个 promise。规定了一种方法,就是在then 里面返回一个新的 promise 称为 promise2
let promise2 = new Promise((resolve, reject)=>{}) 1. 将这个 promise2 返回的值传递到下一个 then 中 2. 如果返回一个普通的值,则将普通的值传递给下一个 then 中
- 当我们在第一个 then 中 return 了一个参数(参数未知,需判断)。这个 return 出来的新的promise 就是 onFulfilled() 或 onRejected() 的值
规定 onFulfilled() 或 onRejected() 的值,即第一个 then 返回的值,叫做 x,判断 x 的函数叫做 resolvePromise
class myPromise {
constructor(executor){...}
then(onFulfilled, onRejected) {
// 声明返回的 promise2
let promise2 = new myPromise((resolve, reject) => {
if (this.state === 'fulfilled') {
let x = onFulfilled(this.value)
resolvePromise(promise2, x, resolve, reject)
}
if (this.state === 'rejected') {
let x = onRejected(this.reason)
resolvePromise(promise2, x, resolve, reject)
}
if (this.state === 'pending') {
this.onResolvedCallbacks.push(() => {
let x = onFulfilled(this.value)
resolvePromise(promise2, x, resolve, reject)
})
this.onRejectedCallbacks.push(() => {
let x = onRejected(this.reason)
resolvePromise(promise2, x, resolve, reject)
})
}
})
// 返回 Promise,完成链式
return promise2
}
}
完成 resolvePromise 函数
规定了一段代码,让不同的 promise 代码互相套用,叫做 resolvePromise
- 如果 x === promise2,会造成循环引用,自己等待自己完成,则报“循环引用”错误
let p = new Promise(resolve => { resolve(0); }); var p2 = p.then(data => { // 循环引用,自己等待自己完成,一辈子完不成 return p2; })
- x 不能是 null
- x 是普通值,直接 resolve(x)
- x 是对象或者函数(默认 promise), let then = x.then
- 如果取 then 报错,则走 reject()
- 如果 then 是个函数,则用 call 执行 then,第一个参数是 x,后面是成功的回调和失败的回调
- 如果成功的回调还是 pormise,就递归继续解析
- 因为成功和失败只能调用一个,所以设定一个 called 来防止多次调用
// 处理 x 的值,如果是普通值直接返回,如果是 promise 则返回 x.then 执行的结果
function resolvePromise(promise2, x, resolve, reject) {
// 如果 new 出来的 Promise2 和 x 是同一个,循环引用报错
if (promise2 === x) return reject(new TypeError('Chaining cycle detected for promise #<Promise>'));
// 先判断是不是对象或者函数
if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
// 调用了成功就不能失败,调用了失败就不能成功,不能多次调用成功或者失败
let called
// 内部可能抛出错误
try {
// 声明 then = x 的 then 方法
let then = x.then
// 如果 then 是函数,就默认是 promise 了
if (typeof then === 'function') {
this.call(x, res => {
// 成功和失败只能调用一个
if (called) return
called = true
// res 可能是一个 promise,递归调用 resolvePromise,直到解析出的值是普通值
resolvePromise(promise2, res, resolve, reject)
}, err => {
// 成功和失败只能调用一个
if (called) return
called = true
// 直接调用 reject 作为失败的结果,并向下传递
reject(err)
})
} else {
// 如果 then 不是函数,就说明是个普通值,直接返回 x
resolve(x)
}
} catch(err) {
// 也属于失败,成功和失败只能调用一个
if (called) return
called = true
// 直接调用 reject 作为失败的结果,并向下传递
reject(err)
}
} else {
// x 是普通值,直接返回
resolve(x)
}
}
解决其他问题
- 规定 onFulfilled 和 onRejected 都是可选参数,如果他们不是函数,必须被忽略
- onFulfilled 返回一个普通的值,成功时直接等于 value => value
- onRejected 返回一个普通的值,失败时如果直接等于 value => value,则会跑到下一个 then 中的 onFulfilled 中,所以直接扔出一个错误 reason => throw err
- 规定 onFulfilled 或 onRejected 不能同步被调用,必须异步调用。我们就用 setTimeout 解决异步问题
- 如果 onFulfilled 或 onRejected 报错,则直接返回 reject()
class myPromise {
constructor(executor) {
this.state = 'pending'
this.value = undefined
this.reason = undefined
this.onResolvedCallbacks = []
this.onRejectedCallbacks = []
let resolve = value => {
if (this.state === 'pending') {
this.state = 'fulfilled'
}
this.value = value
this.onResolvedCallbacks.forEach(fn => fn());
}
let reject = reason => {
if (this.state === 'pending') {
this.state = 'rejected'
}
this.reason = reason
this.onRejectedCallbacks.forEach(fn => fn())
}
try {
executor(resolve, reject)
} catch(err) {
reject(err)
}
}
then(onFulfilled, onRejected) {
// onFulfilled 如果不是函数,忽略 onFulfilled, 直接返回 value
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val
// onRejected 如果不是函数,忽略 onRejected, 让它等于一个函数,并且在函数内继续将 err 向下抛出
onRejected = typeof onRejected === 'function' ? onRejected : err => {throw err}
let promise2 = new Promise((resolve, reject) => {
if (this.state === 'fulfilled') {
// 异步
setTimeout(() => {
try {
let x = onFulfilled(this.value)
resolvePromise(promise2, x, resolve, reject)
} catch(err) {
reject(err)
}
}, 0)
}
if (this.state === 'rejected') {
// 异步
setTimeout(() => {
try {
let x = onRejected(this.reason)
resolvePromise(promise2, x, resolve, reject)
} catch(err) {
reject(err)
}
}, 0)
}
if (this.state === 'pending') {
this.onResolvedCallbacks.push(() => {
// 异步
setTimeout(() => {
try {
let x = onFulfilled(this.value)
resolvePromise(promise2, x, resolve, reject)
} catch(err) {
reject(err)
}
}, 0)
})
this.onRejectedCallbacks.push(() => {
// 异步
setTimeout(() => {
try {
let x = onRejected(this.reason)
resolvePromise(promise2, x, resolve, reject)
} catch(err) {
reject(err)
}
}, 0)
})
}
})
return promise2
}
}
添加 catch 方法
class myPromise {
constructor(executor) {...}
then(onFulfilled, onRejected) {...}
// Promise 中的 catch 指代的就是 then 没有成功回调的一个别名而已
catch(errCallback) {
return this.then(null, errCallback);
}
}
添加 finally 方法
// 无论如何都会执行,把上一个 then 的结果向下传递
// 如果 finally 中返回了一个 Promise 会等待这个 Promise 执行完成后继续执行
myPromise.prototype.finally = function(callback) {
return this.then(val => {
return Promise.resolve(callback()).then(() => val);
}, (err) => {
return Promise.resolve(callback()).then(() => { throw err; });
});
};
添加 resolve、reject、race、all 等方法
// Promise.resolve 会等待里面的 Promise 执行成功
Promise.resolve = val => {
return new Promise((resolve) => {
resolve(val);
});
};
// Promise.reject 不会等待参数中的 Promise 执行完毕
Promise.reject = () => {
return new Promise((_, reject) => {
reject(val);
});
};
// all 方法表示等待所有的 Promise 全部成功后才会执行回调
// 如果有一个 Promise 失败则 Promise 就失败了
Promise.all = promises => {
return new Promise((resolve, reject) => {
// 存放结果
const res = [];
// 计数,当count 等于 length的时候就resolve
let count = 0;
const resolveRes = (index, data) => {
// 将执行结果缓存在res中
res[index] = data;
// 所有子项执行完毕之后,执行resolve 抛出所有的执行结果
if (++count === promises.length) {
resolve(res);
}
};
// 循环遍历每一个参数的每一项
for(let i = 0; i < promises.length; i++) {
const current = promises[i];
// 如果当前项是Promise,则返回 then 的结果
if (isPromise(current)) {
current.then((data) => {
resolveRes(i, data);
}, (err) => {
reject(err);
});
} else {
resolveRes(i, current);
}
}
});
}
// race谁是第一个完成的,就用他的结果
// 如果失败这个 Promise 就失败,如果第一个是成功就是成功
Promise.race = (promises) => {
return new Promise((resolve, reject) => {
for(let i = 0; i < promises.length; i++) {
let current = promises[i];
// 如果是一个 promise 继续执行 then
if (isPromise(current)) {
current.then(resolve, reject);
} else {
// 是普通值则直接 resolve 返回,并终止循环
resolve(current);
break;
}
}
});
}
如何验证我们的 Promise 是否正确?
- 先在后面加上下述代码
// 目前是通过他测试 他会测试一个对象 // 语法糖 Promise.defer = Promise.deferred = function () { let dfd = {} dfd.promise = new Promise((resolve,reject)=>{ dfd.resolve = resolve; dfd.reject = reject; }); return dfd; } module.exports = Promise; //npm install promises-aplus-tests 用来测试自己的 promise 符不符合 promises A+ 规范
- npm 有一个 promises-aplus-tests 插件
// Windows 全局安装 npm i promises-aplus-tests -g // Mac 全局安装 sudo npm i promises-aplus-tests -g
- 命令行 promises-aplus-tests [js文件名],即可验证
注意⚠️:本文 Promise 已满足基本使用,但还是存在一些问题待改进。。。
参考