[md]# 前言
之前虽然也介绍过promise
但是只是告诉promise的用法和特性
c大推荐聊一下为什么出现promise
所以就决定开这一篇番外的短篇了
因为我个人是半路出身的,所以对js的理解并不是特别深入
可能存在事实性错误
欢迎评论指出~
正文
假设我们有一个函数,这个函数存在一些异步的操作
function sleep(){
setTimeout(() => {
console.log('我执行完毕了')
}, 3000);
}
我们想要执行sleep休眠到3s后再执行下一个函数
这个时候如果直接
sleep(3000)
next()
是行不通的
那怎么办?
因为JS里函数是一等公民(可以作为函数参数,可以作为函数返回值,也可以赋值给变量。)
所以我们可以把函数传递给sleep函数,让他在执行3s后再执行传入的函数
function sleep(callback){
setTimeout(() => {
callback()
}, 3000);
}
console.log('我执行了')
sleep(()=>{
console.log('我等待了3s')
})
到这里一切都岁月静好,但是假设我们的层级多了
function sleep(callback) {
setTimeout(() => {
callback();
}, 3000);
}
console.log("我执行了");
sleep(() => {
console.log("我等待了3s");
sleep(() => {
console.log("我等待了6s");
sleep(() => {
console.log("我等待了9s");
sleep(() => {
console.log("我等待了12s");
});
});
});
});
就出现了回调地狱,代码的长度并没有增加,而全部曲折在了一个函数内不断增加宽度。
(补充,当然我们也可以把sleep函数给拆开,分别执行等待3,6,9,12s,这里是将同样的sleep视为不同的step1)
这种代码也被戏称为厄运金字塔
![image](https://bbs.tampermonkey.net.cn/ ... o5596azhh0zz9ac.png)
但是如果我们抽象一下逻辑,发现虽然是回调地狱,实际执行流程是线性的
A-》B-》C-》D
所以我们可以创造一个对象,这个对象来负责管理异步函数,当异步函数执行完毕后执行回调,回调触发这个对象的下一步操作
伪代码大概是这样的
wrapfunc((callback)=>{
callback(1)
}).next((ret)=>{
console.log(ret)//1
return ret+1
}).next((ret)=>{
console.log(ret)//2
return ret+1
})
其实早在很久之前就出现了这种理念,并且称之Promise
所以各语言以及实现的关键字相对一致
js也出现了许多抽象该逻辑的代码库,如
bluebird http://bluebirdjs.com/docs/getting-started.html
promises/A+ https://promisesaplus.com/
jquery https://api.jquery.com/promise/
注意,这个时候已经基本出现了流的概念。
即将代码的执行流程视为一条河流
A-》B-》C-》D
后来官方也实现了promise特性,让我们用Promise来实现一下例子!
function sleep() {
return new Promise((resolve) => setTimeout(resolve, 3000));
}
console.log("我执行了");
sleep().then(()=>{
console.log("我等待了3s");
return sleep()
}).then(()=>{
console.log("我等待了6s");
return sleep()
}).then(()=>{
console.log("我等待了9s");
return sleep()
})
到这里相当于将callback进行了一次解耦
把相同的逻辑给聚合到了一个函数中方便复用以及关注点聚合到了一起
将厄运金字塔的结构已经理成了一个流型结构,但是依然存在大量的回调,这个我们放到后面来谈。
Promise的实现相当于一种依赖反转
即从我们来控制函数的回调变成了我们传入函数和回调
何时进行回调,什么时候开始回调,由Promise这个函数来进行控制
记住这个感觉!
现在我们总结一下
1.Promise是一种通过对异步函数包裹来解决回调地狱的手段
2.Promise符合依赖倒置思想,由我们控制回调,变成了我们传入异步函数和回调函数,Promise来控制回调
3.Promise符合流的概念(即按顺序执行代码块,如A-》B-》C-》D)
相信到这里你已经理解到1和3
我们继续基于2进行推导
既然是由Promise函数来控制回调,那是不是代表我可以添加更多的回调条件,如
传入一个列表,列表全部的函数完成才回调
第一个函数完成就回调
balabalabala
原生统计了一些常见的需求
所以出现了
Promise.all 全部成功回调
Promise.race 第一个成功就回调等需求
当然,只要你想,你可以自己实现一个类似Promise的库
必须N个失败Y个成功才可以开始执行回调~
为什么引入async
如果我们函数需要返回一个promise,我们要得到一个结果
我们就需要不停的创建Promise以及回调,然后返回,再在下方创建一个新的回调
本质上来说相当于一个流式的回调地狱
于是为了解决这种繁琐的声明
出现了async和await
在async函数内可以直接对promise进行阻塞,这样就可以得到一个干净整洁的代码了~
(async function() {
await sleep();
await sleep();
})();
到这里才是真正展平了回调地狱
更多的资料可以参考2楼cxxjackie大神的代码
一些小补充
当声明了一个async表明是异步函数
在异步函数使用await可以对一个promise在当前函数进行阻塞
同时返回也变成了一个promise,代表这个异步函数的结果
因为返回变成了promise
自然async糖也就具有污染性了
另外当使用await的时候如果promise出现reject,我们必须使用try-catch进行捕捉
当然如果大量代码都需要可以使用babel/webpack插件处理
下一代的异步工具库
这里我个人比较看好Rxjs
基本继承了流的概念,同时包含了响应式编程以及函数式编程
算是很符合当下热潮的一个库
如我们注册一个事件监听器
document.addEventListener('click', () => console.log('Clicked!'));
在rxjs将视为一个流的对象,这个流不断地传出事件触发订阅
import { fromEvent } from 'rxjs';
fromEvent(document, 'click').subscribe(() => console.log('Clicked!'));
那我们就基本介绍完毕了~
结语
撒花~
引用文档
https://stackoverflow.com/questions/43712705/why-does-typescript-use-like-types
http://bluebirdjs.com/docs/why-promises.html