登录
转载

JS执行机制与Event Loop

发布于 2020-10-09 阅读 237
  • 前端
  • JavaScript
转载

前言

整理了一下 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、函数每被调用一次,都会产生一个新的执行上下文环境

执行上下文栈

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

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

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 上下文环境

执行上下文的生命周期

image.png

1、创建阶段

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

2、执行阶段

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

3、销毁阶段

  • 执行出栈完毕,等待回收被销毁

javascript 事件循环

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

image.png

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

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 的执行机制

评论区

wex
2粉丝

雾锁山头山锁雾,天连水尾水连天

2

0

0