Open phenomLi opened 6 years ago
这篇文章会帮您解答js中什么是同步什么是异步。
通常来说,在js中,我要按顺序干两件事,这样写就行:
吃饭(); 睡觉();
这样就可以做到先吃饭然后睡觉了。 现在假如吃饭要花费两秒,我想要在吃完饭后睡觉,我这样写:
const eat = function() { console.log('start to eat.'); setTimeout(function() { console.log('finish.'); }, 2000); }; const sleep = function() { console.log('start to go to sleep.'); }; eat(); sleep();
结果却是:
//start to eat. //start to go to sleep. //两秒后 //finish.
为什么会这样呢,我们先说说理论。 异步简单来说就是不按部就班,搞自己的一套。在js中,有一个主线程,还有一个专门执行异步操作的线程,主线程是用来执行代码中的同步部分的,每一个异步函数都会被push到异步队列里面,然后等待主线程所有代码执行完毕,异步队列的函数才会一个一个被弹出执行,直到异步队列为空。也就是说,异步操作会被最后执行。 为什么要这样设计呢?通常在js中,异步函数都是那些带有I/O操作,有阻塞,耗时的函数,如ajax,文件读写(node),数据库操作(node),tcp请求(node),定时器等等。如果这些操作不设置为异步,线程则会一种等待操作完成再进行下一步操作,也就造成了假死(卡死),那么一些语言,像php,则用分发线程来解决耗时的I/O操作,为每一个I/O操作分配一个独立的线程,而node是将异步I/O于事件系统结合。 回到上面的问题,现在大概明白了,原来setTimeout函数被推到了异步队列里面,放到最后执行了。那么是不是问题就没法解决了呢?当然不是,现在我们来这样改造代码:
setTimeout
const eat = function(callback) { console.log('start to eat.'); setTimeout(function() { console.log('finish.'); if(typeof callback === 'function' && callback) { callback(); //回调函数 } }, 2000); }; const sleep = function() { console.log('start to go to sleep.'); }; eat(sleep); //把sleep函数作为一个参数传进eat
现在输出:
//start to eat. //两秒后 //finish. //start to go to sleep.
问题解决。上面的代码中,sleep函数作为了eat的一个参数被传了进去,然后在setTimeout里面被调用,这种被传进异步函数的函数叫做回调函数,回调函数通常用来解决异步函数执行顺序的问题。 在node中使用回调函数是十分普遍的,比如创建一个http服务器或者读取文件操作:
sleep
eat
http.createServer((req, res) => { //... }); fs.readFile('path', data => { //... });
这样的写法一般情况下是没有问题,但是假设有一个场景:在http服务器里面读取一个文件,然后读取完成之后再写入一个文件,最后再打印出写入完成的提示。按照回调函数的写法,我们这样写:
http.createServer((req, res) => { //... fs.readFile('path', data => { //... fs.writeFile('path2', data, () => { console.log('写入完成.'); }); }); });
虽然所代码是没有问题,但这种回调套回调的写法可读性很差,很容易就陷进一层一层的回调地狱。至于这种多个异步函数的特殊情况,现在已经有了许多的解决办法,比如promise,es7的await和async,但是这些知识点说起来又是一潭深水,已经超出了这篇文章要说的范围,或许等以后心血来潮再去慢慢梳理。
promise
await
async
受益匪浅 如获至宝 获益良多 人在美国 刚下飞机
利益相关:共青团员。匿了。
这篇文章会帮您解答js中什么是同步什么是异步。
通常来说,在js中,我要按顺序干两件事,这样写就行:
这样就可以做到先吃饭然后睡觉了。 现在假如吃饭要花费两秒,我想要在吃完饭后睡觉,我这样写:
结果却是:
为什么会这样呢,我们先说说理论。 异步简单来说就是不按部就班,搞自己的一套。在js中,有一个主线程,还有一个专门执行异步操作的线程,主线程是用来执行代码中的同步部分的,每一个异步函数都会被push到异步队列里面,然后等待主线程所有代码执行完毕,异步队列的函数才会一个一个被弹出执行,直到异步队列为空。也就是说,异步操作会被最后执行。 为什么要这样设计呢?通常在js中,异步函数都是那些带有I/O操作,有阻塞,耗时的函数,如ajax,文件读写(node),数据库操作(node),tcp请求(node),定时器等等。如果这些操作不设置为异步,线程则会一种等待操作完成再进行下一步操作,也就造成了假死(卡死),那么一些语言,像php,则用分发线程来解决耗时的I/O操作,为每一个I/O操作分配一个独立的线程,而node是将异步I/O于事件系统结合。
回到上面的问题,现在大概明白了,原来
setTimeout
函数被推到了异步队列里面,放到最后执行了。那么是不是问题就没法解决了呢?当然不是,现在我们来这样改造代码:现在输出:
问题解决。上面的代码中,
sleep
函数作为了eat
的一个参数被传了进去,然后在setTimeout
里面被调用,这种被传进异步函数的函数叫做回调函数,回调函数通常用来解决异步函数执行顺序的问题。在node中使用回调函数是十分普遍的,比如创建一个http服务器或者读取文件操作:
这样的写法一般情况下是没有问题,但是假设有一个场景:在http服务器里面读取一个文件,然后读取完成之后再写入一个文件,最后再打印出写入完成的提示。按照回调函数的写法,我们这样写:
虽然所代码是没有问题,但这种回调套回调的写法可读性很差,很容易就陷进一层一层的回调地狱。至于这种多个异步函数的特殊情况,现在已经有了许多的解决办法,比如
promise
,es7的await
和async
,但是这些知识点说起来又是一潭深水,已经超出了这篇文章要说的范围,或许等以后心血来潮再去慢慢梳理。