l
laor
V1
2023/03/24阅读:26主题:全栈蓝
都2023年了,还不会手写Promise?
在手写Promise
代码前,必须对Promise
语法用法有一定的了解:
❝JavaScript标准内置对象中的
❞Promise
,不懂的戳 MDN
手写Promise
类
Promise
类核心逻辑实现
-
通过 new Promies
创建一个promise
对象,所以Promise
是一个类 ,在执行这个类的时候,需要传递一个执行器进去,并且立即执行它。 -
promise
对象有三种状态,当状态由pending
改变为fulfilled
或rejected
后就不可更改。-
待定( pending
):初始状态,既没有被兑现,也没有被拒绝。 -
已兑现( fulfilled
):意味着操作成功完成。 -
已拒绝( rejected
):意味着操作失败。
-
-
resolve
和reject
函数是用来更改promise
对象的状态
// 参照代码:创建一个Promise实例对象
let promise = new Promise((resolve, reject) => {
resolve("成功")
// reject("失败")
})
// MyPromise类实现
/**
* 等待
*/
const PENDING = "pending"
/**
* 成功
*/
const FULFILLED = "fulfilled"
/**
* 失败
*/
const REJECTED = "rejected"
class MyPromise {
// promise 状态
status = PENDING
constructor(executor) {
executor(this.resolve, this.reject)
}
// 因为resolve和reject都是直接作为一个函数调用,使用箭头函数定义是为了让函数内部的this指向Promies实例
resolve = (value) => {
// 如果状态不是等待,阻止程序向下执行
if (this.status !== PENDING) return
// 状态改为成功
this.status = FULFILLED
}
reject = (reason) => {
// 如果状态不是等待,阻止程序向下执行
if (this.status !== PENDING) return
// 状态改为失败
this.status = REJECTED
}
}
-
promise
执行then
方法-
可以传递两个参数分别做为成功和失败回调; -
then
方法内部做的事情就判断状态;如果状态是成功,调用成功的回调函数;如果状态是失败,调用失败回调函数。
-
// 参照代码:then方法
promise.then(
(value) => {},
(reason) => {}
)
class MyPromise {
// 添加一个原型方法代码
then(successCallback, failCallback) {
if (this.status === FULFILLED) {
successCallback()
} else if (this.status === REJECTED) {
failCallback()
}
}
}
-
then
成功回调有一个参数,表示成功之后的值;then
失败回调有一个参数,表示失败后的原因。所以要记录成功的值或者失败的原因。
class MyPromise {
// 成功之后的值
value = undefined
// 失败之后的值
reason = undefined
resolve = (value) => {
// 增加如下代码,保存成功的 值
this.value = value
}
reject = (reason) => {
// 增加如下代码,保存失败的原因
this.reason = reason
}
// 在成功失败回调中传递成功的值或者失败的原因
then(successCallback, failCallback) {
if (this.status === FULFILLED) {
successCallback(this.value)
} else if (this.status === REJECTED) {
failCallback(this.reason)
}
}
}
至此。恭喜你,一个最简单的Promise
类已经实现了。
Promise
中加入异步逻辑
先分析如下的例子:
let mypromise = new MyPromise((resolve) => {
setTimeout(() => {
resolve("成功")
}, 2000);
})
mypromise.then(value => {
console.log(value);
}, reason => {
console.log(reason);
})
-
创建 MyPromise
的实例对象,它的执行器会立即执行,发现一个setTimeout
,立即执行。 -
setTimeout
的回调是异步代码,2s后执行。此时它不会阻塞主线程代码执行 -
然后立即执行 mypromise.then
方法,但此时状态不为pending
,既不会执行成功回调也不会执行失败对调,所以2s后resolve
时要想执行对应的回调就得把对应的回调函数保存起来。
// 代码实现, 增加如下代码
class MyPromise {
// 成功回调
successCallback = undefined
// 失败回调
failCallback = undefined
resolve = (value) => {
// 增加如下代码
this.successCallback(this.value)
}
reject = (reason) => {
// 增加如下代码,失败回调是否存在,存在时,执行它
this.failCallback(this.reason)
}
// then 增加pending状态的逻辑
then(successCallback, failCallback) {
if (this.status === FULFILLED) {
} else if (this.status === REJECTED) {
} else {
this.successCallback = successCallback
this.successCallback = failCallback
}
}
}
实现then
方法多次调用
-
同一个 promise
对象下面的then
方法是可以被调用多次的;
如下代码中,同步resolve
时,status
已经为fulfilled
,会直接执行successCallback
,无需更改;reject
同理
promise.then(value => { console.log(value); })
promise.then(value => { console.log(value); })
异步resolve
时,只需要将多个回调函数改为保存在数组中,需要的时候再去依次执行即可;reject
同理,修改代码如下。
class MyPromise {
// 成功回调修改为数组
successCallback = []
// 失败回调修改为数组
failCallback = []
resolve = (value) => {
// 成功回调是否存在,存在时,依次执行它
while (this.successCallback.length) this.successCallback.shift()(this.value)
}
reject = (reason) => {
// 失败回调是否存在,存在时,依次执行它
while (this.failCallback.length) this.failCallback.shift()(this.reason)
}
then(successCallback, failCallback) {
// 等待,将成功和失败回调存储到数组中
this.successCallback.push(successCallback)
this.failCallback.push(failCallback)
}
}
then
方法的链式调用
-
then
方法是可以被链式调用的, 后面then
方法的回调函数的参数的是上一个then
方法回调函数的返回值-
then
方法链式调用。只需要在then
方法中返回一个新的Promise
实例即可 -
将上一个 then
方法的返回值x传给下一个then
回调函数-
x是普通值:直接调用 resolve
-
x是 promise
对象:查看promsie
对象返回的结果,再根据这个结果决定调用新的将要返回的promise
的resolve
还是reject
-
-
status
为rejectd
时。处理同上。 -
status
为pending
时(异步),只需要对successCallback
或者failCallback
用一个新的函数包装一层,函数内部逻辑处理同上。
-
function resolvePromise(x, resolve, reject) {
if (x instanceof MyPromise) { // promise对象
x.then(resolve, reject)
} else {
// 普通值
resolve(x)
}
}
class MyPromise {
then(successCallback, failCallback) {
const promise2 = new MyPromise((resolve, reject) => {
if (this.status === FULFILLED) {
const x = successCallback(this.value)
// 判断 x 的值是普通值还是promise对象
// 如果是普通值 直接调用resolve
// 如果是promise对象 查看promsie对象返回的结果
// 再根据promise对象返回的结果 决定调用这个新promise的resolve 还是reject
resolvePromise(x, resolve, reject)
} else if (this.status === REJECTED) {
const x = failCallback(this.reason)
resolvePromise(x, resolve, reject)
} else {
this.successCallback.push(() => {
const x = successCallback(this.value)
resolvePromise(x, resolve, reject)
})
this.failCallback.push(() => {
const x = failCallback(this.reason)
resolvePromise(x, resolve, reject)
})
}
})
return promise2
}
resolve = (value) => {
// 移除this.value参数传递
while (this.successCallback.length) this.successCallback.shift()()
}
reject = (reason) => {
// 移除this.reason参数传递
while (this.failCallback.length) this.failCallback.shift()()
}
}
链式调用识别promise
对象自返回
// 自返回示例代码
let promise = new Promise((resolve) => {
resolve("success")
})
let p1 = promise.then((v) => {
return p1
})
p1.then(()=>{},e=>{
console.log(e.message); // Chaining cycle detected for promise #<Promise>
})
其实逻辑很简单,promise2
是我们要返回的新的promise
,如果他和上一个promise
的successCallback
返回的结果x一样,就是自返回,因此只需要判断promise2
和 x 是否相等即可。
function resolvePromise(promise2, x, resolve, reject) {
// 先判断自返回
if (promise2 === x) {
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
}
if (x instanceof MyPromise) { // promise对象
x.then(resolve, reject)
} else {
// 普通值
resolve(x)
}
}
class MyPromise {
then(successCallback, failCallback) {
const promise2 = new MyPromise((resolve, reject) => {
if (this.status === FULFILLED) {
setTimeout(() => {
const x = successCallback(this.value)
resolvePromise(promise2, x, resolve, reject)
}, 0)
} else if (this.status === REJECTED) {
setTimeout(() => {
const x = failCallback(this.reason)
resolvePromise(promise2, x, resolve, reject)
}, 0)
} else {
this.successCallback.push(() => {
setTimeout(() => {
const x = successCallback(this.value)
resolvePromise(promise2, x, resolve, reject)
}, 0)
})
this.failCallback.push(() => {
setTimeout(() => {
const x = failCallback(this.reason)
resolvePromise(promise2, x, resolve, reject)
}, 0)
})
}
})
return promise2
}
}
捕获错误
-
执行构造器的执行器函数时的错误捕获
constructor(executor) {
try {
executor(this.resolve, this.reject)
} catch (error) {
this.reject(error)
}
}
-
then
回调里面的错误捕获。只需要将对应代码块使用trycatch
包起来
setTimeout(() => {
try {
const x = failCallback(this.reason)
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)
then方法的参数变成可选参数
-
当 then
方法没有参数的时候,只需要手动补一个参数就可以。
promise
.then()
.then()
.then(value => console.log(value))
// 将上面代码转换一下如下即可
promise
.then(value => value)
.then(value => value)
.then(value => console.log(value))
// 代码实现
then(successCallback, failCallback) {
successCallback = successCallback ? successCallback : value => value
failCallback = failCallback ? failCallback : reason => {throw reason}
}
Promise.all静态方法
用来解决异步并发问题:
-
允许 promise
按照异步代码调用的顺序得到异步代码执行的结果 -
返回值也是一个 promise
对象 -
所有成功才成功,只有一个失败就失败
static all(array) {
let result = []
let finishCtn = 0
return new MyPromise((resolve, reject) => {
/**
* 将下标为key的元素添加到result当中
*/
function addData(key, value) {
result[key] = value
finishCtn++;
// 当数组所有的项已经有结果了就resolve
if (finishCtn === array.length) {
resolve(result)
}
}
for (let i = 0; i < array.length; i += 1) {
let current = array[i]
if (current instanceof MyPromise) {
current.then(value => addData(i, value), (reason) => {
reject(reason)
})
} else {
//普通值
addData(i, current)
}
}
})
}
Promise.resolve静态方法
-
将给定的值转换成 promise
对象 -
如果传入的值是普通值,则转换成 promise
对象;如果已经是promise
对象,则原封不动返回
static resolve(value) {
if (value instanceof MyPromise) return value
return new MyPromise(resolve => resolve(value))
}
finally方法实现
-
无论当前 promise
的状态是成功还是失败,finally
里面的回调始终会被执行 -
可以在 finally
方法的后面链式调用then
方法拿到当前promise
最终返回的结果
// 基础版
finally(callback) {
// 1.通过then方法可以拿到当前promise最终的状态
return this.then((value) => {
callback()
return value // 2.返回值给下一个promise接收
}, (reason) => {
callback()
throw reason
})
}
上面的代码存在一个问题,当finally回调里面返回一个promise
对象时,应该等待这个promise
执行结束,再返回当前promise
对象的执行结果
// 改良版
finally(callback) {
return this.then((value) => {
// 通过MyPromise.resolve进行promise化,然后在then里面返回value
return MyPromise.resolve(callback()).then(() => value)
}, (reason) => {
return MyPromise.resolve(callback()).then(() => { throw reason })
})
}
catch方法实现
内部调用then
方法,不设置成功回调,将catch
的回调作为失败回调传递下去。
catch(failCallback){
return this.then(undefined,failCallback)
}
完整代码
/**
* 等待
*/
const PENDING = "pending"
/**
* 成功
*/
const FULFILLED = "fulfilled"
/**
* 失败
*/
const REJECTED = "rejected"
function resolvePromise(promise2, x, resolve, reject) {
// 先判断自返回
if (promise2 === x) {
return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
}
if (x instanceof MyPromise) { // promise对象
x.then(resolve, reject)
} else {
// 普通值
resolve(x)
}
}
class MyPromise {
// promise 状态
status = PENDING
// 成功之后的值
value = undefined
// 失败之后的值
reason = undefined
// 成功回调
successCallback = []
// 失败回调
failCallback = []
constructor(executor) {
try {
executor(this.resolve, this.reject)
} catch (error) {
this.reject(error)
}
}
// 因为resolve和reject都是直接作为一个函数调用,使用箭头函数定义是为了让函数内部的this指向Promies实例
resolve = (value) => {
// 如果状态不是等待,阻止程序向下执行
if (this.status !== PENDING) return
// 状态改为成功
this.status = FULFILLED
// 保存成功的值
this.value = value
// 成功回调是否存在,存在时,执行它
while (this.successCallback.length) this.successCallback.shift()()
}
reject = (reason) => {
// 如果状态不是等待,阻止程序向下执行
if (this.status !== PENDING) return
// 状态改为失败
this.status = REJECTED
// 保存失败的原因
this.reason = reason
// 失败回调是否存在,存在时,执行它
while (this.failCallback.length) this.failCallback.shift()()
}
then(successCallback, failCallback) {
successCallback = successCallback ? successCallback : value => value
failCallback = failCallback ? failCallback : reason => { throw reason }
const promise2 = new MyPromise((resolve, reject) => {
if (this.status === FULFILLED) {
// 为了拿到promise2这里加一个异步逻辑,
setTimeout(() => {
try {
const x = successCallback(this.value)
// 判断 x 的值是普通值还是promise对象
// 如果是普通值 直接调用resolve
// 如果是promise对象 查看promsie对象返回的结果
// 再根据promise对象返回的结果 决定调用这个新Promise的resolve 还是调用reject
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)
} else if (this.status === REJECTED) {
setTimeout(() => {
try {
const x = failCallback(this.reason)
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)
} else {
// 等待,将成功和失败回调存储起来
this.successCallback.push(() => {
setTimeout(() => {
try {
const x = successCallback(this.value)
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)
})
this.failCallback.push(() => {
setTimeout(() => {
try {
const x = failCallback(this.reason)
resolvePromise(promise2, x, resolve, reject)
} catch (error) {
reject(error)
}
}, 0)
})
}
})
return promise2
}
finally(callback) {
return this.then((value) => {
// 通过MyPromise.resolve进行promise化,然后在then里面返回value
return MyPromise.resolve(callback()).then(() => value)
}, (reason) => {
return MyPromise.resolve(callback()).then(() => { throw reason })
})
}
catch(failCallback){
return this.then(undefined,failCallback)
}
static all(array) {
let result = []
let finishCtn = 0
return new MyPromise((resolve, reject) => {
/**
* 将下标为key的元素添加到result当中
*/
function addData(key, value) {
result[key] = value
finishCtn++;
// 当数组所有的项已经有结果了就resolve
if (finishCtn === array.length) {
resolve(result)
}
}
for (let i = 0; i < array.length; i += 1) {
let current = array[i]
if (current instanceof MyPromise) {
current.then(value => addData(i, value), (reason) => {
reject(reason)
})
} else {
//普通值
addData(i, current)
}
}
})
}
static resolve(value) {
if (value instanceof MyPromise) return value
return new MyPromise(resolve => resolve(value))
}
}
作者介绍
l
laor
V1