持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第7天,点击查看活动详情
js作为一门单线程的语言,但是在很多情况下却又有多线程的需求,这个时候就只能靠通过异步来进行处理。再推出ajax、node之后异步的身影就更加可见了。文件操作,网络请求等等这些都是要进行异步处理的,了解异步的整个发展历史也就变得很有必要了。
回调
最开始的时候异步是通过回调的形式来进行的。
可以看到我们通过传递一个函数给我们的readFile方法,它就会在合适的时候调用这个函数,并且这个函数接受两个参数第一个是err,第二个是传递给我们的数据data,这里我们可以看到运行结果是先输出我们的第7句,才输出我们的第五句,如果是同步的话它应该是按顺序输出的,这里的合适的时候就说明了异步其实就是不确定性(其实异步也叫不确定性)。
可以看到这样的异步也很好理解,那么为什么后面异步还要进行发展呢?随着需求的改变,异步的场景越来越复制,我们可以需要对获取的data进行处理传递给另一个异步请求,获取到的数据data2又继续传递给下一个异步回调......,那这样我们的回调就只能耦合在一起,可阅读性也会变得很差,更有可能产生回调地狱,代码横向发展(我们的代码习惯应该的纵向发展的),就不那么优雅了。
promise
ES6的时候引入了Promise来处理异步:
可以看到上述代码已经简单的完成了我们刚才异步回调的任务,Promise是一个构造函数接受一个执行器,这个回调函数接受两个参数一个是成功后的回调(也是一个函数),第二个是失败后的回调,在请求失败的时候调用,到这里我们可能还不能很好的发现它的有点,反而觉得有点复杂了。确实毕竟我们的需求比较简单体现不出它的优点,我们回到刚才的回调地狱,都是promise可以解决回调地狱,我们来看看它是怎么解决的。
可以看到我们的代码是向下发展的,不是原来回调地狱横向发展的形式,Promise还有一个优点就是它使得异步回调函数的定义更加的灵活了,原来我们传递回调函数的时候就要把获取到数据后怎么处理的逻辑一起加入,而现在我们可以在then之后再来设计我们的处理逻辑,但是这样的方法还是不是很符合我们同步的编码习惯。
async/await
这个可以说是解决回调地狱的终极形式了,但是要明确一点它不是取代promise,它和promise的关系是相辅相成的。
可以看到如上的代码就实现了我们上面的需求,await只能用在async函数中,且async函数的返回值一定是promise形式的。如果处理刚才的回调地狱我们只需要这样:
可以发现这中写法其实和同步已经没有什么区别了。
其他
我们很多的API都是回调形式版本的,可否封装一个方法,把我们的回调版本转换成可以通过async/await处理的版本呢?代码如下:
const fs = require("fs");
function changeToAsync(func,props){
return new Promise((resolve,reject)=>{
func(props,(err,data)=>{
if(err!==null){
reject(err);
}else{
resolve(data);
}
})
})
}
async function test(){
let data = await changeToAsync(fs.readFile,"./test.png");
console.log(data);
}
test();