这是我参与更文挑战的第29天,活动详情查看: 更文挑战
首先简单说一下什么是同步什么是异步。
因为JavaScript是单线程的,所以同步就是执行某一个代码的时候,在该代码没有执行完毕之前,其他代码是无法执行的,如果前面那段代码没执行完,会阻塞后面的代码执行,这种就叫做同步;异步就是某一段代码在异步(不是主线程,比如调用浏览器的api用浏览器的线程)调用后,这段代码不会立刻得到结果,一般是通过回调函数处理这个调用之后等待同步任务完成后执行,这样的情况叫异步。
JavaScript为什么需要异步
因为如果没有异步,JS又是单线程的,如果都是同步代码的话很容易就造成阻塞后面的代码执行。如果使用异步的话就可以先等待异步代码的执行结果,同步代码执行完之后直接执行异步代码的结果就行,这样就不会导致阻塞,因此在JS编程中,会大量使用异步编程。
异步编程的发展历程
回调函数
回调函数是早年为了实现JS的异步编程,一般都采用回调函数的方式,比如比较典型的回调,或是setTimeout/setInterval来进行一些异步编程的操作,但是回调函数可能会导致一个问题,那就是回调地狱
fs.readFile(A, 'utf-8', function(err, data) {
fs.readFile(B, 'utf-8', function(err, data) {
fs.readFile(C, 'utf-8', function(err, data) {
fs.readFile(D, 'utf-8', function(err, data) {
//....
});
});
});
});
复制代码
上面这个代码的意思就是先读A的文本,再根据A文本读B,根据B读C,依次向下。但是上面的代码很容易形成回调地狱。
回调实现异步的场景有很多:
- ajax请求的回调
- 定时器的回调
- 事件的回调
- Node.js的一些方法回调
异步回调如果层级少,可读性和维护性还能介绍,但是一旦层级变多就会陷入回调地狱,就乱套了,因此就用上了Promise了。
Promise
为了解决这种问题,社区提出了Promise,而后ES6将Promise写入了语言标准,使用Promise可以在一定程度上解决回调地狱的问题。
还是跟上面实现相同的功能
function read(url) {
return new Promise((resolve,reject)=>{
fs.readFile(url,'utf8',(err,data)=>{
if(err) reject (err);
resolve(data);
});
});
}
read(A).then(data => {
return read(B);
}).then(data => {
return read(C);
}).then(data => {
return read(D);
}).catch(reason => {
console.log(reason);
});
复制代码
可读性提高了,但是这种链式没有从根本解决问题,可读性提高了,但是还是难维护,Promise还提供了一个all的方法。对于这种情景用all的效果可能效果更好。
function read(url){
return new Promise((resolve,reject)=>{
fs.readfile(url,'utf8',(err,data)=>{
if(err) reject(err)
resolve(data)
})
})
}
Promise.all([read(A),read(B),read(C)]).then(data=>{
console.log(data);
}).catch(err=>
console.log(err)
)
)
复制代码
all方法就是实现多个异步并行执行。