## 1、异步与同步

#### 1)同步

  同步是代码自上到下执行,一个任务执行完后,才能执行下一个任务。同步的优势显而易见:可以使任务有序进行,不会造成资源上处理上的混乱。且任务有序进行较好的处理了任务之间的依赖性,如后一个任务需要前一个任务的结果。如果多个任务处理同一个资源,不会造成资源处理的混乱。但是如果遇到其中某一段代码需要等待很长时间才能执行完毕,比如:setTimeoutajax等等,那么使用同步的操作可能就会很影响代码执行的效率。所以遇到这样的任务,我们需要异步来实现。

####2)异步

“`

console.log(1)

setTimeout(function(){

  console.log(2)

},2000)

console.log(3)

“`

看上面这段代码,一个非常简单的定时器的操作,这便是一个最简单的异步的实现。我们会发现,假如这段代码以同步的方式运行,那么在等待setTimeout到达时间被触发之前,Javascript引擎不能做任何动作,只能在原地等待setTimeout执行后,再继续执行后面的代码。这样做便会造成极大的浪费。所以Js引擎正确的执行流程是:遇到异步操作,就将它放在一边,继续执行后面的代码,在这个异步操作执行结束后,会自动触发它的一个回调函数,将回调函数放入Event Loop中。在J引擎执行完主线程上的全部任务后,再来执行Event Loop中的任务。

## 2Event LoopMacro Task Micro Task

#### (1)Event Loop

刚才说到,Javascript引擎除了主线程之外,还有一个事件队列(Event Loop),事件队列便是Js异步编程中的核心。为了更好的理解Event Loop,请看下图:

![](http://image.beekka.com/blog/2014/bg2014100802.png)

Event Loop 是一个Javascript运行时的一个概念,主线程运行的时候,产生堆(heap)和栈(stack),栈中的代码调用各种外部API,它们在任务队列中加入各种事件(clickloaddone)。只要栈中的代码执行完毕,主线程就会去读取任务队列,依次执行那些事件所对应的回调函数。

那么我们来看一下这段程序的执行顺序:

“`

// 1. 开始执行

console.log(‘script start’); // 2. 打印字符串 “script start”

setTimeout(

    function() {                 // 5. 浏览器在 0ms 之后将该函数推入任务队列

                                 //    而到第5步时才会被主线程执行

      console.log(‘setTimeout’); // 6. 打印字符串 “setTimeout”

    },

    0

);                       // 3. 调用 setTimeout 函数,并定义其完成后执行的回调函数

console.log(‘script end’);   // 4. 打印字符串 “script end”

// 5. 主线程执行栈清空,开始读取 任务队列 中的任务

“`

以上就是浏览器的异步任务的执行机制,核心点为:

异步任务是由浏览器执行的,不管是AJAX请求,还是setTimeout API,浏览器内核会在其它线程中执行这些操作,当操作完成后,将操作结果以及事先定义的回调函数放入 JavaScript 主线程的任务队列中

JavaScript 主线程会在执行栈清空后,读取任务队列,读取到任务队列中的函数后,将该函数入栈,一直运行直到执行栈清空,再次去读取任务队列,不断循环

当主线程阻塞时,任务队列仍然是能够被推入任务的。这也就是为什么当页面的 JavaScript 进程阻塞时,我们触发的点击等事件,会在进程恢复后依次执行。

(2) macro Tasks & Micro Tasks

我们发现,setTimeoutPromise这两个我们最常见的异步任务,却分别属于两个不同的task,那么来看一下,他们到底有什么不同。

“`

console.log(‘script start’);

setTimeout(function() {

  console.log(‘setTimeout’);

}, 0);

Promise.resolve().then(function() {

  console.log(‘promise1’);

}).then(function() {

  console.log(‘promise2’);

});

console.log(‘script end’);

“`

在这里,setTimeout的延时为0,而Promise.resolve()也是返回一个被resolvepromise对象,即这里的then方法中的函数也是相当于异步的立即执行任务,那么他们到底是谁在前谁在后?

“`

“script start”

“script end”

“promise1”

“promise2”

“setTimeout”

“`

这里的运行结果是Promise的立即返回的异步任务会优先于setTimeout延时为0的任务执行。

原因是任务队列分为 macrotasks microtasks,而Promise中的then方法的函数会被推入 microtasks 队列,而setTimeout的任务会被推入 macrotasks 队列。

注:在每一次事件循环中,macrotask 只会提取一个执行,而 microtask 会一直提取,直到 microtasks 队列清空。