phenomLi / Blog

Comments, Thoughts, Conclusions, Ideas, and the progress.
219 stars 17 forks source link

说说js中的异步 #3

Open phenomLi opened 6 years ago

phenomLi commented 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函数被推到了异步队列里面,放到最后执行了。那么是不是问题就没法解决了呢?当然不是,现在我们来这样改造代码:

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的awaitasync,但是这些知识点说起来又是一潭深水,已经超出了这篇文章要说的范围,或许等以后心血来潮再去慢慢梳理。

Ctrl-Ling commented 5 years ago

受益匪浅 如获至宝 获益良多 人在美国 刚下飞机

phenomLi commented 5 years ago

受益匪浅 如获至宝 获益良多 人在美国 刚下飞机

利益相关:共青团员。匿了。