本文旨在探讨Promise 出现背景、实现原理以及常用方法的实现。本文不是Promise的基本教程,如有不了解的读者,可以参考Promise mdn[1] 。
在前端开发中,我们经常都会使用 setTimeout, XHR等方式进行异步调用,比如以下一个简单请求例子:
//ajax 是封装的XHR请求函数,此处省略具体实现
ajax({
method: 'GET',
url: '/url',
data: {
id: 'xxx'
}
}, (response) => {
// do something;
})
通常会在请求结束之后进行数据处理,比如修改dom 的展示等等;单个实现比较简单直接;但是在实际业务场景中,并不是单一的数据请求,往往会遇到下一个请求依赖前置的请求。比如我们先要获取商品的信息id, 然后去查询详情数据,我们通常处理会如下:
ajax({method: 'GET',
url: '/get',
data: {
id: 'xxx'
}
}, (response) => {
// do something;
ajax({}, () => {
//...
})
})
如果依赖层次过多的话会出现多层嵌套,对于代码的易读性和可维护性来说都是很大的挑战。Promise 的出现很大程度解决上述问题。我们可以具体来看看Promise实现原理。
Promise 主要通过以下两步来解决回调嵌套问题:
实现回调函数的延时绑定。创建Promise对象p1 后,我们可以在任何地方使用p1.then 来执行具体的回调,实现回调函数的延时绑定:
p1 = new Promise((resovle, reject) => {
// 指定具体的异步操作
setTimeout(() => {
resovle('promise')
}, 1000)
});
// 通过then 方法绑定回调函数
p1.then((data) => {
console.log(data);
})
将回调函数onResovle的返回值穿透到外层。假定onResovle返回值为returnVal
,分为以下两种情况:
returnVal
不为Promise对象,那么使用returnVal
作为then 返回的新Promise的值。returnVal
是Promise对象, 则会等该Promise对象(即returnVal
)的状态发生变化才发生调用,并且新的Promise 对象状态和 returnVal
状态相同。let p1 = new Promise((resovle, reject) => {
//执行业务逻辑
resovle(100)
});
//p1 通过then 方法延迟绑定 onResovle 方法
const onResovle = (data) => {
console.log(data);
let p2 = new Promise((resovle, reject) => {
resovle(data + 1);
});
return p2;
}
let p2 = p1.then(onResovle);
p2.then((data) => {
console.log(data);
console.log(p2);
})
具体实现可以参考Promise实现原理[2]
Promise 本身拥有一些静态方法,比如 resovle, reject, race, all 等,其实现有很多,本文选择all 来展开具体实现:
那么了解到三个点后,可以容易的写出相关代码:
Promise.all2 = (promises) {
return new Promise((resovle, reject) => {
let result = [];
let promiseResovleCnt = 0;
for(let i = 0; i < promises.length; i++) {
//需注意数组中的结果顺序和promises顺序一致
promises[i].then((data) => {
promiseResovleCnt++
result[i] = data;
if(promiseResovleCnt === promises.length) {
resovle(result);
}
}, (reason) => {
// 只要其中1个执行出错,那么Promise.all不在继续执行promises数组中其他对象
reject(reason);
})
}
})
}
本文主要介绍了以下内容:
如文中有错误之处,欢迎留言斧正。
[1] Promise mdn: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise
[2] Promise实现原理:https://juejin.cn/post/6844903665686282253#heading-2
[3] 极客时间· 浏览器原理与实践: https://time.geekbang.org/column/intro/216