事件循环

一、关于javascript
javascript是一门 单线程 语言,在最新的HTML5中提出了Web-Worker,但javascript是单线程这一核心仍未改变。因此一切javascript版的"多线程"都是用单线程模拟出来的,一切javascript多线程都是纸老虎!
二、javascript事件循环
既然js是单线程,那就像只有一个窗口的银行,客户须要排队一个一个办理业务,同理js任务也要一个一个顺序执行。若是一个任务耗时过长,那么后一个任务也必须等着。那么问题来了,假如咱们想浏览新闻,可是新闻包含的超清图片加载很慢,难道咱们的网页要一直卡着直到图片彻底显示出来?所以聪明的程序员将任务分为两类:javascript

JS分为同步任务和异步任务
同步任务都在主线程上执行,造成一个执行栈
主线程以外,事件触发线程管理着一个任务队列,只要异步任务有了运行结果,就在任务队列之中放置一个事件。
一旦执行栈中的全部同步任务执行完毕(此时JS引擎空闲),系统就会读取任务队列,将可运行的异步任务添加到可执行栈中,开始执行java

事件循环是经过任务队列的机制来进行协调的。一个 Event Loop 中,能够有一个或者多个任务队列(task queue),一个任务队列即是一系列有序任务(task)的集合;每一个任务都有一个任务源(task source),源自同一个任务源的 task 必须放到同一个任务队列,从不一样源来的则被添加到不一样队列。 setTimeout/Promise 等API即是任务源,而进入任务队列的是他们指定的具体执行任务。程序员

宏任务:promise

macro)task(又称之为宏任务),能够理解是每次执行栈执行的代码就是一个宏任务(包括每次从事件队列中获取一个事件回调并放到执行栈中执行)。

浏览器为了可以使得JS内部(macro)task与DOM任务可以有序的执行,会在一个(macro)task执行结束后,在下一个(macro)task 执行开始前,对页面进行从新渲染,流程以下:
(macro)task->渲染->(macro)task->...
(macro)task主要包含:script(总体代码)、setTimeout、setInterval、I/O、UI交互事件、postMessage、MessageChannel、setImmediate(Node.js 环境)

微任务:浏览器

microtask(又称为微任务),能够理解是在当前 task 执行结束后当即执行的任务。也就是说,在当前task任务后,下一个task以前,在渲染以前。

因此它的响应速度相比setTimeout(setTimeout是task)会更快,由于无需等渲染。也就是说,在某一个macrotask执行完后,就会将在它执行期间产生的全部microtask都执行完毕(在渲染前)。

microtask主要包含:Promise.then、MutaionObserver、process.nextTick(Node.js 环境)

运行机制:多线程

在事件循环中,每进行一次循环操做称为 tick,每一次 tick 的任务处理模型是比较复杂的,但关键步骤以下:

执行一个宏任务(栈中没有就从事件队列中获取)
执行过程当中若是遇到微任务,就将它添加到微任务的任务队列中
宏任务执行完毕后,当即执行当前微任务队列中的全部微任务(依次执行)
当前宏任务执行完毕,开始检查渲染,而后GUI线程接管渲染
渲染完毕后,JS线程继续接管,开始下一个宏任务(从事件队列中获取)

流程图以下:
图片描述异步

两道题 让你所有理解事件循环async

console.log('1')
  setTimeout(function() {
    console.log('2')
    process.nextTick(function() {
      console.log('3')
    })
    new Promise(function(resolve) {
      console.log('4')
      resolve()
    }).then(function() {
      console.log('5')
    })
  })
  process.nextTick(function() {
    console.log('6')
  })
  new Promise(function(resolve) {
    console.log('7')
    resolve()
  }).then(function() {
    console.log('8')
  })
  setTimeout(function() {
    console.log('9')
    process.nextTick(function() {
      console.log('10')
    })
    new Promise(function(resolve) {
      console.log('11')
      resolve()
    }).then(function() {
      console.log('12')
    })
  })
  
  第二题
async function async1() {
    console.log('async1 start');
    await async2();
    console.log('async1 end');
}
async function async2() {
    console.log('async2');
}
console.log('script start');
setTimeout(function() {
    console.log('setTimeout');
}, 0)
async1();
new Promise(function(resolve) {
    console.log('promise1');
    resolve();
}).then(function() {
    console.log('promise2');
});
console.log('script end');

注意:
async function async1() {                        
    console.log('async1 start');
    await async2();                   
    console.log('async1 end');
}
等同于
async function async1() {
    console.log('async1 start');
    Promise.resolve(async2()).then(() => {
            console.log('async1 end');
    })
}