ThinkSSL🔒 一键申购 5分钟快速签发 30天无理由退款 购买更放心 广告
## 一、概述 JavaScript是单线程模型,同时只能执行一个任务,其他任务都必须在后面排队等待; 好处:简单; 坏处:只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行;常见的浏览器无响应(假死),往往就是因为某一段 JavaScript 代码长时间运行(比如死循环),导致整个页面卡在这个地方,其他任务无法执行; ## 二、同步任务和异步任务 同步任务:那些没有被引擎挂起、在主线程上排队执行的任务。只有前一个任务执行完毕,才能执行后一个任务; 异步任务:那些被引擎放在一边,不进入主线程、而进入任务队列的任务;只有引擎认为某个异步任务可以执行了(比如 Ajax 操作从服务器得到了结果),该任务(采用回调函数的形式)才会进入主线程执行。排在异步任务后面的代码,不用等待异步任务结束会马上运行,也就是说,异步任务不具有“堵塞”效应; 举例: Ajax 操作可以当作同步任务处理,也可以当作异步任务处理,由开发者决定。如果是同步任务,主线程就等着 Ajax 操作返回结果,再往下执行;如果是异步任务,主线程在发出 Ajax 请求以后,就直接往下执行,等到 Ajax 操作有了结果,主线程再执行对应的回调函数; ## 三、异步模式 ### 回调函数 回调函数:把一个函数A当做实参专递给另外一个函数B,在B方法执行的时候,把A执行了,我们把这种机制叫做 “回调函数机制”; 回调函数是异步操作最基本的方法; 回调函数的优点是简单、容易理解和实现,缺点是不利于代码的阅读和维护,各个部分之间高度耦合,使得程序结构混乱、流程难以追踪(尤其是多个回调函数嵌套的情况),而且每个任务只能指定一个回调函数; ~~~ function f1() { // ... } function f2() { // ... } f1(); f2(); ~~~ 上面代码的问题在于,如果`f1`是异步操作,`f2`会立即执行,不会等到`f1`结束再执行; 可以考虑改写`f1`,把`f2`写成`f1`的回调函数: ~~~ function f1(callback) { // ... callback(); } function f2() { // ... } f1(f2); ~~~ ### 事件监听 采用事件驱动模式。异步任务的执行不取决于代码的顺序,而取决于某个事件是否发生; 首先,为`f1`绑定一个事件: ~~~ f1.on('done', f2); ~~~ 当`f1`发生`done`事件,就执行`f2`; ~~~ function f1() { setTimeout(function () { // ... f1.trigger('done'); }, 1000); } ~~~ 上面代码中,`f1.trigger('done')`表示,执行完成后,立即触发`done`事件,从而开始执行`f2`; 这种方法的优点是比较容易理解,可以绑定多个事件,每个事件可以指定多个回调函数,而且可以去耦合,有利于实现模块化。缺点是整个程序都要变成事件驱动型,运行流程会变得很不清晰。阅读代码的时候,很难看出主流程; ### 发布/订阅 事件完全可以理解成“信号”,如果存在一个“信号中心”,某个任务执行完成,就向信号中心“发布”(publish)一个信号,其他任务可以向信号中心“订阅”(subscribe)这个信号,从而知道什么时候自己可以开始执行。这就叫做”[发布/订阅模式],又称“[观察者模式](observer pattern); ## 四、定时器 JavaScript 提供定时执行代码的功能,叫做定时器(timer),主要由`setTimeout()`和`setInterval()`这两个函数来完成。它们向任务队列添加定时任务; ## 五、Promise Promise 对象是 JavaScript 的异步操作解决方案,为异步操作提供统一接口; Promise 可以让异步操作写起来,就像在写同步操作的流程,而不必一层层地嵌套回调函数; Promise 的设计思想是,所有异步任务都返回一个 Promise 实例。Promise 实例有一个`then`方法,用来指定下一步的回调函数; ~~~ var p1 = new Promise(f1); p1.then(f2); ~~~ 上面代码中,`f1`的异步操作执行完成,就会执行`f2`; Promise 的优点在于,让回调函数变成了规范的链式写法,程序流程可以看得很清楚。它有一整套接口,可以实现许多强大的功能,比如同时执行多个异步操作,等到它们的状态都改变以后,再执行一个回调函数;再比如,为多个回调函数中抛出的错误,统一指定处理方法等等; 而且,Promise 还有一个传统写法没有的好处:它的状态一旦改变,无论何时查询,都能得到这个状态。这意味着,无论何时为 Promise 实例添加回调函数,该函数都能正确执行。所以,你不用担心是否错过了某个事件或信号。如果是传统写法,通过监听事件来执行回调函数,一旦错过了事件,再添加回调函数是不会执行的; Promise 的缺点是,编写的难度比传统写法高,而且阅读代码也不是一眼可以看懂。你只会看到一堆`then`,必须自己在`then`的回调函数里面理清逻辑; >[danger] > Promise 的回调函数属于异步任务,会在同步任务之后执行; > Promise 的回调函数不是正常的异步任务,而是微任务(microtask)。它们的区别在于,正常任务追加到下一轮事件循环,微任务追加到本轮事件循环。这意味着,微任务的执行时间一定早于正常任务;