JS执行机制与Event Loop

epeppanda
发布于 2021-4-22 10:55
浏览
0收藏

前言


整理了一下 javascript 的基础知识,在此给大家做下分享,喜欢的大佬们可以给个赞。

js 是一门单线程语言。 js 引擎有一个主线程(main thread)用来解释和执行 js 程序,实际上还存在其他的线程。例如:处理 ajax 请求的线程、处理 DOM 事件的线程、定时器线程、读写文件的线程(例如在 node.js 中)等等。这些线程可能存在于 js 引擎之内,也可能存在于 js 引擎之外,在此我们不做区分。不妨叫它们工作线程。

 

JS 执行上下文


当代吗运行时,会产生对应的运行环境,在这个环境中,所有的变量都会备实现提出来(变量提升),有的直接赋值,有的默认赋值,有点默认值 undefined ,代码从上而下开始执行,就叫做执行上下文。

 

1、变量提升

foo // undefined
var foo = function () {
  console.log('foo1')
}

foo() // foo1, foo赋值

var foo = function () {
  console.log('foo2')
}

foo() // foo2, foo 赋值

 

2、函数提升

foo() // foo2
function () {
  console.log('foo1')
}

foo() // foo2

function foo () {
  console.log('foo2')
}
foo() // foo2

 

3、声明优先级,函数 > 变量

foo() // foo2
var foo = function () {
  console.log('foo1')
}

foo() // foo1, foo 重新赋值

function foo () {
  console.log('foo2')
}

foo() // foo1

 

运行环境


在 javascript 的世界中,运行环境有三种,分别是:

1、全局环境:代码首先进入环境
2、函数环境:函数被调用时执行的环境
3、eval 函数:(不常用)

 

执行上下文特点
1、单线程,在主线程上进行
2、同步执行,从上往下按顺序执行
3、全局上下文只有一个,浏览器关闭时会被弹出栈
4、函数执行上下文没有数目限制
5、函数每被调用一次,都会产生一个新的执行上下文环境

 

执行上下文栈


执行全局代码时,会产生一个执行上下文环境,每次调用函数都又会长生一个执行上下文环境。当函数调用完成时,这个上下文环境以及其中的数据都会被消除,再重新回到全局上下文环境,处于活动状态的执行上下文环境只有一个。

其实这是一个压栈出栈的过程————执行上下文栈

JS执行机制与Event Loop-鸿蒙开发者社区

var // 1.进入全局上下文环境
  a = 10,
  fn,
  bar = function (x) {
    var b = 20
    fn(x + b) // 3.进入 fn 上下文环境
  }

fn = function (y) {
  var c = 20
  console.log(y + c)
}

bar(5) // 2.进入 bar 上下文环境

 

执行上下文的生命周期

JS执行机制与Event Loop-鸿蒙开发者社区

1、创建阶段

生成变量对象
建立作用域链
确定 this 指向


2、执行阶段

变量赋值
函数引用
执行其他代码


3、销毁阶段

执行出栈完毕,等待回收被销毁
javascript 事件循环
同步和异步任务分别进入不同的执行“场所”,同步的进入主线程,异步的进入 Event Table 并注册函数
当指定事件完成时, Event Table 会将这个函数移入 Event Queue


主线程内的任务执行完毕为空,会去 Event Queue 读取对应的函数,进入主线程执行
上述过程会不断重复,也就是常说的 Event Loop (事件循环)JS执行机制与Event Loop-鸿蒙开发者社区

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

 

macro-task (宏任务)


可以理解是每次执行栈执行的代码就是一个宏任务(包括每次从事件队列中获取一个事件回调并放到执行栈中执行)

浏览器为了能够使得 JS 内部 (macro)task 与 DOM 任务能够有序执行,会在一个 (macro)task 执行结束后,在下一个 (macro)task 执行开始前,对页面进行重新渲染

(macro)task 主要包含: script (整体代码)、setTimeout、setInterval、I/O、UI 交互事件、postMessage、MessageChannel、setImmediate(Node.js 环境)

 

micro-task(微任务)


可以理解是在当前 task 执行结束后立即执行的任务。也就是说,在当前的 task 任务后,下一个 task 之前,在渲染之前。所以他的响应熟读比 setTimeout 会更快,因为无需等渲染。也就是说,在摸一个 macrotask 执行完后,就会将在它执行期间产生的所有 mocrotask 都执行完毕(在渲染前)。

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

 

举个例子


我们来分析一段比较复杂的代码,看看你是否真的掌握了 js 的执行机制

consoloe.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')
  })
})
// 1, 7, 8, 2, 4, 5, 6, 3, 9, 11, 12, 10

 

又一个例子

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('sacript end')

// script start
// async1 start
// async2
// promise1
// script end
// async end
// promise2
// setTimeout

 

解决异步的方法


1、回调函数

ajax('x1', () => {
  // callback 函数体
  ajax('x2', () => {
    // callback 函数体
    ajax('x3', () => {
      // callback 函数体
    })
  })
})

 

优点:解决了同步的问题
缺点:回调地狱,不能用 try catch 捕捉错误,不能 return


2、Promise 为了解决 callback 的问题而产生


Promise 实现了链式调用,也就是说每次 then 后返回的都是一个全新的 Promise,如果我们在 then 中 return,return 的结果会被 Promise.reolve() 包装

优点:解决了回调地狱
缺点:无法取消 Promise,错误需要通过回调函数来捕获


3、Async/await


优点是:代码清晰,不用 Promise 写一大堆 then 链,处理了回调地狱问题
缺点:await 将异步代码改造成同步代码,如果多个异步操作没有依赖性而使用 await 会导致性能上的降低


总结


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

分类
标签
已于2021-4-22 10:55:44修改
收藏
回复
举报
回复
    相关推荐