1

JavaScript中线程运行机制详解

对于JavaScript我们都知道,他是个单线程语言,但是准确来说它是拥有一个执行程序主线程,和消息队列辅线程(Event Loop),以及各个真正处理异步操作的工作线程
当主线程执行JS程序的时候,如果遇到了异步的操作(如:click事件,setTimeOut,I/0操作,异步请求,Promise等),主线程将各个异步任务交给工作线程,主线程继续执行,工作线程会判断是否到了要执行该异步操作的条件了,如果工作线程判断已经到了要执行工作线程的条件之后,就会把回调函数放入辅线程Event Loopmacrotask任务队列或者microtask任务队列的队尾。当上述主线程执行完后续同步调用的代码之后,辅线程会一直循环上面两个队列,首先执行的是microtask任务队列,而且该任务队列是该次循环就会执行完并清空的,macrotask会等到下次event loop循环的时候执行。

Example

1.  console.log(1);
2.  setTimeout(function timerCallback(){
3.    console.log(2);
4.  },0)
5.  new Promise(function execute(resolve,reject){
6.     console.log(3);
7.     if(true)
8.         resolve();
9.  }).then(function resolveCallback(){
10.     console.log(4);
11. });
12. console.log(5);
// 上面程序执行的顺序是:1,3,5,4,2

代码详解:当主线程执行上述代码的时候,从上往下执行
1、先将console.log(1)这句代码做一个入栈执行的操作,打印出1,该句代码没有后续的方法调用了,就出栈销毁

2、执行setTimeout,将改句代码入栈执行,判断到该方法属于WebAPIs(JS工作线程提供API的统称,其中包含了webcore工作模块提供了DOM Binding、network、timer等),timerCallback方法出栈,交给工作线程webcore下的timer模块,这里timer模块会判断是否到了执行条件了,由于是延时是0,所以timer模块判断可以执行了,并将回调函数timerCallback放到辅助线程Event Loop的macrotask队尾,等待下一次Event Loop循环去执行。

3、继续执行Promise这句代码(包括then语句),将其入栈执行,判断该Promise属于Promise模块(非WebAPIs),将其then的回调函数交给Promise模块,有后续调用;将execute方法入栈执行,有后续调用;将console.log(3)入栈执行,打印出3,console.log(3)执行完毕,出栈销毁;有后续调用if(true)入栈执行,判断为true,出栈销毁,继续将resolve()入栈执行,判断resolve()是WebAPIs的成功的回调,执行的时候通知到上面的Promise模块,Promise模块将成功的回调放到Event Loop的microtask队尾,resolve()出栈销毁,无后续调用,Promise出栈销毁。

4、继续执行,将console.log(5)入栈执行,打印出5,无后续调用,出栈销毁。

5、Event Loop开始循环microtask队列,将resolveCallback()入栈执行,有后续调用;将console.log(4)入栈执行,打印出4,出栈销毁;无后续调用,resolveCallback()出栈销毁,清空队列。

6、Event Loop开始循环macrotask队列,将timerCallback()入栈执行,有后续调用;将console.log(2)入栈执行,打印出2,出栈销毁,无后续调用;将timerCallback()出栈销毁,清空队列。

备注:

macrotask任务队列的来源有:

  • setTimeout

  • setInterval

  • setImmediate

  • I/O

  • UI rendering

microtask任务队列的来源有:

  • process.nextTick

  • promises

  • Object.observe

  • MutationObserver


夜里的太阳
469 声望16 粉丝

炫耀从来不是我的动机,好奇才是