Blog
Blog copied to clipboard
说说js中的异步
这篇文章会帮您解答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`函数被推到了异步队列里面,放到最后执行了。那么是不是问题就没法解决了呢?当然不是,现在我们来这样改造代码:
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服务器或者读取文件操作:
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,但是这些知识点说起来又是一潭深水,已经超出了这篇文章要说的范围,或许等以后心血来潮再去慢慢梳理。
受益匪浅 如获至宝 获益良多 人在美国 刚下飞机
受益匪浅 如获至宝 获益良多 人在美国 刚下飞机
利益相关:共青团员。匿了。