让前端飞

javascript 并发模型与事件循环

2019-02-12  本文已影响0人  EdmundChen

JavaScript 的并发模型基于“事件循环”。

一、运行时概念

可视化描述

Javascript执行引擎的主线程运行的时候,产生堆(heap)和栈(stack),程序中代码依次进入栈中等待执行,若执行时遇到异步方法,该异步方法会被添加到用于回调的队列(queue)中【即JavaScript执行引擎的主线程拥有一个执行栈/堆和一个任务队列

function foo(b) {
  var a = 10;
  return a + b + 11;
}

function bar(x) {
  var y = 3;
  return foo(x * y);
}

console.log(bar(7)); // 返回 42

当调用 bar 时,创建了第一个帧 ,帧中包含了 bar 的参数和局部变量。当 bar 调用 foo 时,第二个帧就被创建,并被压到第一个帧之上,帧中包含了 foo 的参数和局部变量。当 foo 返回时,最上层的帧就被弹出栈(剩下 bar 函数的调用帧 )。当 bar 返回的时候,栈就空了。

二、事件循环(Event Loop)

Event Loop

【注:因为主线程从”任务队列”中读取事件的过程是循环不断的,因此这种运行机制又称为Event Loop(事件循环)

console.log(1);
setTimeout(function() {
    console.log(2);
},5000);
console.log(3);
//输出结果:
//1
//3
//2
解释:

三、微任务(Macrotask) 和 宏任务(Microtask)

Event Loop 2
不同的任务源会被分配到不同的 Task 队列中,任务源可以分为 微任务(microtask)宏任务(macrotask)。在 ES6 规范中,microtask 称为 jobs,macrotask 称为 task。
Event Loop 执行顺序如下所示:
实例代码
console.log('script start')

async function async1() {
  await async2()
  console.log('async1 end')
}
async function async2() {
  console.log('async2 end')
}
async1()

setTimeout(function() {
  console.log('setTimeout')
}, 0)

new Promise(resolve => {
  console.log('Promise')
  resolve()
})
  .then(function() {
    console.log('promise1')
  })
  .then(function() {
    console.log('promise2')
  })

console.log('script end')
// script start 
// async2 end 
// Promise 
// script end 
// promise1 
// promise2 
// async1 end 
// setTimeout
微任务包括: promiseMutationObserver
宏任务包括: scriptsetTimeoutsetIntervalsetImmediateI/OUI rendering

这里很多人会有个误区,认为微任务快于宏任务,其实是错误的。因为宏任务中包括了 script ,浏览器会先执行一个宏任务,接下来有异步代码的话才会先执行微任务。

四、永不阻塞

事件循环模型的一个非常有趣的特性是,与许多其他语言不同,JavaScript 永不阻塞。 处理 I/O 通常通过事件和回调来执行,所以当一个应用正等待一个 IndexedDB 查询返回或者一个 XHR请求返回时,它仍然可以处理其它事情,比如用户输入。

遗留的例外是存在的,如 alert 或者同步 XHR,但应该尽量避免使用它们。

参考: https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Using_promises
前端面试之道
https://blog.kaolafed.com/2017/04/21/JavaScript%E5%B9%B6%E5%8F%91%E6%A8%A1%E5%9E%8B%E4%B8%8EEvent%20Loop/

上一篇 下一篇

猜你喜欢

热点阅读