ES5处理异步

2,394 阅读5分钟

1.什么是异步

- 异步(async)是相对于同步(sync)而言的
    - 同步就是一件事一件事的执行。只有前一个任务执行完毕,才能执行后一个任务。而异步就比如
        ```js
            console.log(1)

            setTimeout(function(){

                console.log(2)

            },100)

            console.log(3)

            //这里打印顺序是 1  3   2
        ```
    - setTimeout就是一个异步任务,当JS引擎顺序执行到setTimeout的时候发现他是个异步任务,则会把这个任务挂起,继续执行后面的代码。

    - 那么为什么要使用异步呢?
        由于javascript是单线程的,只能在JS引擎的主线程上运行的,所以js代码只能一行一行的执行,不能在同一时间执行多个js代码任务,这就导致如果有一段耗时较长的计算,比如ajax请求等IO操作,如果没有异步的存在,就会出现用户长时间等待

    所以我们认为异步,就比如我们请求服务端的数据,我们发送完请求后就继续执行下面的代码,把发送请求加入到异步队列中,当服务端处理完数据后返回结果,异步队列通知我们,我们在进行数据处理,这样的话,数据处理就可以相互不影响,多条线齐头并进。什么是异步**
- 异步(async)是相对于同步(sync)而言的
    - 同步就是一件事一件事的执行。只有前一个任务执行完毕,才能执行后一个任务。而异步就比如
        ```js
            console.log(1)

            setTimeout(function(){

                console.log(2)

            },100)

            console.log(3)

            //这里打印顺序是 1  3   2
        ```
    - setTimeout就是一个异步任务,当JS引擎顺序执行到setTimeout的时候发现他是个异步任务,则会把这个任务挂起,继续执行后面的代码。

    - 那么为什么要使用异步呢?
        由于javascript是单线程的,只能在JS引擎的主线程上运行的,所以js代码只能一行一行的执行,不能在同一时间执行多个js代码任务,这就导致如果有一段耗时较长的计算,比如ajax请求等IO操作,如果没有异步的存在,就会出现用户长时间等待

    所以我们认为异步,就比如我们请求服务端的数据,我们发送完请求后就继续执行下面的代码,把发送请求加入到异步队列中,当服务端处理完数据后返回结果,异步队列通知我们,我们在进行数据处理,这样的话,数据处理就可以相互不影响,多条线齐头并进。

2.ES5怎么处理异步呢?

- **回调函数**
        回调是一个函数被作为一个参数传递到另一个函数里,在那个函数执行完后再执行。( 也即:B函数被作为参数传递到A函数里,在A函数执行完后再执行B )

        假设 有两个函数 f1和f2 后者等待前者的执行结果。
        ```js
            function f1(callback){
              setTimeout(function () {
                // f1的任务代码
                callback();
              }, 1000);
            }
            // 执行
            f1(f2)
        ```
        采用这种方式,我们把同步操作变成了异步操作,f1不会堵塞程序运行,相当于先执行程序的主要逻辑,将耗时的操作推迟执行。

- **事件监听的方式**
        - 采用事件驱动模式。
        - 任务的执行不取决代码的顺序,而取决于某一个事件是否发生。
        - 这样的话比较容易理解,可以绑定多个事件,每一个事件可以指定回调函数,事件触发函数执行
        - 监听函数有:on,bind,listen,addEventListener,observe

        - 比如addEventListener方法

            ```html
                <div id="id1">
                    <div id="id2"></div>
                </div>
            ```
        - js代码

            ```js
                id1.addEventListener("click",function(){console.log('id1');},false);
                id2.addEventListener("click",function(){console.log('id2');},false);
                //点击id=id2的div,先在sonsole中输出,先输出id2,在输出id1

                id1.addEventListener("click",function(){console.log('id1');},false);
                id2.addEventListener("click",function(){console.log('id2');},true);
                //点击id=id2的div,先在sonsole中国输出,先输出id1,在输出id2
            ```

- **发布订阅的方式**
      - 我们假定,存在一个"信号中心",某个任务执行完成,就向信号中心"发布"(publish)一个信号,其他任务可以向信号中心"订阅"(subscribe)这个信号,从而知道什么时候自己可以开始执行。

      我们采用的是Ben Alman的Tiny Pub/Sub,这是jQuery的一个插件演示一下

      ```js
        // f2向"信号中心"jQuery订阅"done"信号。
        jQuery.subscribe("done", f2);

        function f1(){
        &emsp;&emsp;setTimeout(function () {
        &emsp;&emsp;&emsp;&emsp;// f1的任务代码
        &emsp;&emsp;&emsp;&emsp;jQuery.publish("done");
        &emsp;&emsp;}, 1000);
        }
        // f1执行完,发布信号,f2收到信号后,执行即可

3.ES5异步处理有什么问题?

    - 回调函数的优点是简单、容易理解和部署,缺点是不利于代码的阅读和维护,各个部分之
    间高度耦合(Coupling),流程会很混乱,而且每个任务只能指定一个回调函数。

    - 事件监听的方式优点是比较容易理解,可以绑定多个事件,每个事件可以指定多个回调函
    数,而且可以“去耦合”(Decoupling),有利于实现模块化。缺点是整个程序都要变成事件驱动型,运行流程会变得很不清晰。

    - 这种方法的性质与”事件监听”类似,但是明显优于后者。因为我们可以通过查看”消息中心
    ”,了解存在多少信号、每个信号有多少订阅者,从而监控程序的运行。