引子
去年写了一篇有关promise的手写文章,写到一半发现自己的理解还不是很透彻,写的很烂,今年卷土重来,实现部分采用功能分解,目录跳转的形式呈现,力求最通俗易懂得剖析promise,我们开始吧。
通过本文你能学到什么:
- 为什么需要用到promise
- 链式调用的概念及如何实现
- 发布订阅模式在promise中的应用
- 递归思想
- 一步步带你分解功能,手写实现一个promise,包括静态方法:all、allSettled、race、resolve、reject
- 如何测试自己写的promise是否符合规范
1. 认识promise
promise是ES6原生实现的一种解决异步编程的方案,我们都知道js是一门单线程语言,使用异步编程(事件和回调函数)的方式进行定时器任务、网络请求等。不了解promise用法的同学建议看看这一篇《阮一峰 ECMAScript 6 (ES6) 标准入门教程 第三版 - promise对象》。
✅举个例子:
看一个简单的异步任务定时器的例子:
// 模拟异步任务
function fn(s, callback) {
setTimeout(() => {
console.log(s);
callback(s);
}, 2000);
}
// 第一种: 使用回调函数
fn("1", (s) => {
s += "2";
fn(s, () => {
s += "3";
fn(s, () => {
s += "4";
fn(s, () => {
s += "5";
fn(s, () => {
console.log("result:", s);
console.log('done')
});
});
});
});
});
// 第二种: 使用promise
function promiseFactory(str) {
return new Promise((resolve, reject) => {
fn(str, resolve);
});
}
promiseFactory("1")
.then((res) => {
res += "2";
return promiseFactory(res);
})
.then((res) => {
res += "3";
return promiseFactory(res);
})
.then((res) => {
res += "4";
return promiseFactory(res);
})
.then((res) => {
res += "5";
return promiseFactory(res);
})
.then((res) => {
console.log("result:", res);
console.log('done')
});
上面代码中两种方法都输出:
1
12
123
1234
12345
result: 12345
done
在第一种写法中可以看到回调层层嵌套,阅读起来十分困难,这还只是个简单的例子,实际中的代码可能涉及多种异步任务相互嵌套,形成回调地狱。
在第二种写法中使用了promise,采用链式调用的方式代替嵌套,虽然代码量看起来更多了一些,但在学习了promise的用法之后,阅读起来轻松了许多,更符合人从上到下线性阅读的习惯。而这正是promise诞生的原因。