JavaScript执行机制 --事件循环

1.关于javascript

javascript是一门 单线程 语言,在最新的HTML5中提出了Web-Worker,但javascript是单线程这一核心仍未改变。所以一切javascript版的"多线程"都是用单线程模拟出来的,一切javascript多线程都是纸老虎!

2.JavaScript 事件循环 Event Loop

javascript 上任务分为两种,分别为同步任务和异步任务。

同步任务:
   在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务
异步任务:
   不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行

在掘金上盗了一张图
JavaScript执行机制 --事件循环_第1张图片
** 导图要表达的内容用文字来表述的话:**

  1. 同步和异步任务分别进入不同的执行"场所",同步的进入主线程,异步的进入Event Table并注册函数
  2. 当指定的事情完成时,Event Table会将这个函数移入Event Queue。
  3. 主线程内的任务执行完毕为空,会去Event Queue读取对应的函数,进入主线程执行。
  4. 上述过程会不断重复,也就是常说的Event Loop(事件循环)。

js引擎存在monitoring process进程,会持续不断的检查主线程执行栈是否为空,一旦为空,就会去Event Queue那里检查是否有等待被调用的函数。

1_example

$.ajax({
	url:'xxx',
	data:{},
	success: (res)=>{
		console.log('发送成功');
	}
});
console.log('代码执行结束');
// 代码执行结束    发送成功
** 上面一段简易的ajax请求 分析**
 - ajax进入Event Table,注册回调函数success。
 - 执行console.log('代码执行结束')- ajax事件完成,回调函数success进入Event Queue。
 - 主线程从Event Queue读取回调函数success并执行。

2_example

我们还经常遇到setTimeout(fn,0)这样的代码,0秒后执行又是什么意思呢?是不是可以立即执行呢?

答案是不会的,setTimeout(fn,0)的含义是,指定某个任务在主线程最早可得的空闲时间执行,意思就是不用再等多少秒了,只要主线程执行栈内的同步任务全部执行完成,栈为空就马上执行。举例说明:

console.log("先执行这里");
setTimeout(() =>{
 console.log('执行setTimeout');
},0);
console.log('执行结束');
//先执行这里
//执行结束
//执行setTimeout

3_example

上面说完了setTimeout,当然不能错过它的孪生兄弟setInterval。他俩差不多,只不过后者是循环的执行。对于执行顺序来说,setInterval会每隔指定的时间将注册的函数置入Event Queue,如果前面的任务耗时太久,那么同样需要等待。

唯一需要注意的一点是,对于setInterval(fn,ms)来说,我们已经知道不是每过ms秒会执行一次fn,而是每过ms秒,会有fn进入Event Queue。一旦setInterval的回调函数fn执行时间超过了延迟时间ms,那么就完全看不出来有时间间隔了。这句话请读者仔细品味。

let t = +new Date();
setInterval(()=>{
	while((+new Date() -t)<3000){
		//此处模拟睡眠3秒钟
	}
	console.log('11111');
},1000)
// 控制台会3秒后 立刻输出2个11111  后续会恢复正常 1秒打印一次11111

除了广义的同步任务和异步任务,我们对任务有更精细的定义:

macro-task(宏任务):
可以理解是每次执行栈执行的代码就是一个宏任务(包括每次从事件队列中获取一个事件回调并放到执行栈中执行)
包括整体代码script,setTimeout,setInterval ,setImmediate(Node环境支持)

micro-task(微任务):
可以理解是在当前task执行结束后立即执行的任务
包括 Promise.then,promise.catch ,promise.finally, process.nextTick(Node环境支持)
宏任务与微任务关系图
JavaScript执行机制 --事件循环_第2张图片

4_example

不同类型的任务会进入对应的Event Queue,比如setTimeout和setInterval会进入相同的Event Queue。

事件循环的顺序,决定js代码的执行顺序。进入整体代码(宏任务)后,开始第一次循环。接着执行所有的微任务。然后再次从宏任务开始,找到其中一个任务队列执行完毕,再执行所有的微任务

setTimeout(()=>{
	console.log('setTimeout');
},1000);
new Promise(resolve =>{
	console.log("promise");
	resolve();
}).then(function(){
	console.log('then');
});
console.log('console');
// promise
//console
//then
//setTimeout

 - 先遇到setTimeout,那么将其回调函数注册后分发到宏任务Event Queue。
 - 接下来遇到了Promise,new Promise立即执行,then函数分发到微任务Event Queue。
 - 遇到console.log(),立即执行。
 - 整体代码script作为第一个宏任务执行结束,看看有哪些微任务?我们发现了then在微任务Event Queue里面,执行。
 - 第一轮事件循环结束了,我们开始第二轮循环,当然要从宏任务Event Queue开始。我们发现了宏任务Event
Queue中setTimeout对应的回调函数,立即执行。 结束

5_example

console.log(1);
setTimeout(()=>{
  console.log(2);
  new Promise(resolve =>{
  	console.log(3);
  	resolve();
  }).then(()=>{
  	console.log(4);
  });
},0);

new Promise(resolve =>{
  console.log(5);
  resolve();
}).then(()=>{
  console.log(6);
});

setTimeout(()=>{
  console.log(7);
  new Promise(resolve=>{
  	console.log(8);
  	resolve();
  }).then(()=>{
  	console.log(9);
  })
},0);

console.log(10);
// 1 5 10 6 2 3 4 7 8 9

第一轮事件循环
1.整体script作为第一个宏任务进入主线程,遇到console.log(1);
2.遇到setTimeout, 将其回调函数分发到宏任务Event Queue中,暂且记为timer1
3.遇到promise ,new Promise直接执行,输出5。 遇到then 被分发到微任务Event Queue中,记为then1
4.遇到setTimeout, 将其回调函数分发到宏任务Event Queue中,暂且记为timer2
5.遇到console.log(10) 输出10
关系如图所示:

宏任务 微任务
timer1 then1
timer2

到这里 第一轮宏任务执行完成 ,看下微任务列表中then1 一个微任务,执行 输出6 。第一轮事件执行完成依次输出 : 1 5 10 6
第二次事件循环
从宏任务列表中timer1 开始:

  1. 遇到console.log(2) 输出2
  2. 遇到new promise 直接执行,输出3, 遇到then 分发到微任务Event Queue中,记为then2
    3.第二个宏任务执行完成,查看是否有微任务,发现有一个then2的微任务 ,输出 4
    关系图如下
宏任务 微任务
then2

到这里 第二轮事件循环执行完成,输出: 2 3 4
第三次事件循环
从宏任务列表中timer2 开始:

  1. 遇到console.log(7) 输出7
  2. 遇到new promise 直接执行,输出8, 遇到then 分发到微任务Event Queue中,记为then3
  3. 第二个宏任务执行完成,查看是否有微任务,发现有一个then3的微任务 ,输出9
    关系图如下
宏任务 微任务
then3

到这里 第三轮事件循环执行完成,输出: 7 8 9
最后整体输出为: 1 5 10 6 2 3 4 7 8 9 (此结果只是浏览器环境输出结果,Node环境输出略有不同,与执行方式有关)

Nodejs中的事件循环

  参照阮一峰大神  [JavaScript 运行机制详解:再谈Event Loop](http://www.ruanyifeng.com/blog/2014/10/event-loop.html)

写在最后

  1. javascript是一门单线程语言
  2. Event Loop是javascript的执行机制

你可能感兴趣的:(javascript,javascript执行机制,事件循环)