js事件循环

console.log('begin');
setTimeout(function() { console.log('timeout') });
new Promise(function(resolve) {
    for (let i = 0; i < 3; i++) {
        if (i == 1) resolve();
        console.log(i);
    }
    console.log('promise')
}).then(function() {
    console.log('then')
})
console.log('end');

上述代码依次输出:begin、0、一、二、promise、end、then、timeoutajax

js的一个特色就是单线程,可是不少时候咱们仍然须要在不一样的时间去执行不一样的任务,例如给元素添加点击事件,设置一个定时器,或者发起ajax请求,所以须要一个异步机制来达到这样的目的,事件循环机制也所以而来。promise

每个js程序都拥有惟一的事件循环,大多数代码的执行顺序是能够根据函数调用栈的规则执行的,而setTimeout/setInterval或者不一样的事件绑定(click、mousedown等)中的代码,则经过队列来执行。浏览器

任务队列又分为宏任务(macro-task)与微任务(micro-task)两种,在浏览器中,包括:异步

  • macro-task:script(总体代码)、setTimeout/setInterval、I/O、UI rendering等
  • micro-task:Promise、MutationObserver

事件循环的顺序,决定了js代码的执行顺序:
首先从macro-task中的script开始第一次循环。此时全局上下文进入函数调用栈,直到调用栈清空,在这个过程当中,若是遇到任务分发器(例如timer、promise),就会将任务放入对应队列中去函数

第一次循环时,macro-task中其实只有script,所以函数调用栈清空以后,会直接执行全部的micro-task。当全部可执行的micro-task执行完毕以后,就表示第一次事件循环结束线程

第二次循环会再次从macro-task开始执行。此时macro-task中的script队列已经没有任务了,可是可能会有其余的队列任务,而micro-task中暂时尚未任务。此时会先选择其中一个宏任务队列,例如setTimeout,将该队列中全部的任务所有执行完毕,而后再执行此过程当中可能产生的微任务。微任务执行完毕以后,再回过头来执行其余宏任务队列中的任务。以此类推,直到全部宏任务队列中的任务都被执行了,而且清空了微任务,第二次循环就会结束code

若是在第二次循环过程当中,产生了新的宏任务队列,或者以前宏任务队列中的任务暂时没有知足执行条件,例如延迟时间不够或者事件没有被触发,那么将会继续以一样的顺序重复循环server