前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >老生常谈之事件循环

老生常谈之事件循环

作者头像
饼干_
发布2022-09-19 15:17:56
2950
发布2022-09-19 15:17:56
举报

theme: channing-cyan

前言

JS 的事件循环也算是一个老生常谈的话题了,面试中相信大部分人都有被问到:说一说 JS 的事件循环。那么要了解他的循环机制,首先我们要了解一些基本概念。今天我们就简单聊聊事件循环然后向外拓展。

单线程的 JS

进程 && 线程

  • 进程是系统分配的独立资源,是 CPU 资源分配的基本单位,进程是由一个或者多个线程组成的。
  • 线程是进程的执行流,是CPU调度和分派的基本单位,同个进程之中的多个线程之间是共享该进程的资源的。

我们知道 JS 他是单线程,也就是说每次只能执行一项任务,其他任务都得按照顺序排队等待被执行,只有当前的任务执行完成之后才会往下执行下一个任务。

JS 在 Node 和浏览器中执行机制是不一样的,浏览器 Event Loop 是 HTML 中定义的规范,Node Event Loop 是由 libuv 库实现,这里主要讲的是浏览器部分。

JS 事件循环中有两种任务(同步任务、异步任务)

  • 同步任务:在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务。
  • 异步任务:不进入主线程而进入任务队列的任务,只有任务队列通知主线程某个异步任务完成了,该任务才会进入主线程。

任务队列

那在任务队列里存放的各种事件又是怎么个情况?首先他们分为宏任务和微任务。

宏任务

微任务

谁发起的

宿主(Node、浏览器)

JS引擎

具体事件

script (可以理解为外层同步代码)、setTimeout/setInterval、UI rendering/UI事件、postMessage,MessageChannel、setImmediate,I/O(Node.js)

Promise.then、MutaionObserver

谁先运行

后运行

先运行

会触发新一轮Tick吗

不会

事件循环

事件循环流程:

  • 所有同步任务都在主线程上执行,形成一个执行栈。
  • 主线程外,还存在一个任务队列。只要异步任务有了运行结果,就在任务队列里放置一个事件(回调)。
  • 当执行栈中的同步任务执行完后,系统就会读取任务队列里的事件,那些对应的异步任务结束等待状态,进入执行栈开始执行。
  • 主线程不断重复以上步骤。

浏览器内核

浏览器的内核是多线程的,一个浏览器一般至少实现三个常驻线程:

  • JS 引擎
  • GUI 渲染线程
  • 事件触发线程

这里大概了解一下不做重点叙述。

例题

来一道简单的题目理解一下:

No.1

代码语言:javascript
复制
const promise = new Promise((resolve, reject) => {
  console.log(1);
  resolve('success')
  console.log(2);
});
promise.then(() => {
  console.log(3);
});
console.log(4);
  1. 从上至下,先遇到new Promise,执行其中的同步代码1
  2. 再遇到resolve('success'), 将promise的状态改为了resolved并且将值保存下来
  3. 继续执行同步代码2
  4. 跳出promise,往下执行,碰到promise.then这个微任务,将其加入微任务队列
  5. 执行同步代码4
  6. 本轮宏任务全部执行完毕,检查微任务队列,发现promise.then这个微任务且状态为resolved,执行它。

异步的历史

我们面试中经常问起的 Promise 相关题目都是跟 JS 的循环事件机制有关的,Promise 是 ES6 的产物,在还没有 Promise 时的远古时期我们使用回调只能用 callback(回调函数)。

callback

代码语言:javascript
复制
function back(callback) {
    // do something ...
    callback()
}
back(() => {
    console.log('hello')
})

若是要嵌套回调,就会让代码难以维护。

代码语言:javascript
复制
back(() => {
    back({
        () => {
            // ....
        }
    })
})

Promise

这时候 Promist 被提出来,最早是由社区提出和实现的,Es6 将他写进规范并提供原生对象。在一定程度上解决了回调地狱。

代码语言:javascript
复制
function back(data) {
    return new Promise((resolve, reject) => {
        // do something ...
        if(data) resolve(data)
        else reject(data)
    })
}

back('hello').then(res => {
    // do something ...
    return back('hi')
}).then(res => {
    return back('nihao')
}).catch(err => {
    console.log(err)
})

但 Promise 也并非十全十美,虽然大多情况他是 OK 的,但是 Promise 一旦执行了便无法取消,且错误不能被 try catch 捕获。

Generator

Generator 算是一个小插曲,是 ES6 提供的异步编程解决方案,他就是一个封装的异步任务或容器,异步操作需要暂停的地方,都用 yield 语句声明。

Generator 函数一般配合 yield 或 Promise 使用,他返回的是迭代器。

代码语言:javascript
复制
function* back() {
    let a = yield 1
    console.log(a)
    let b = yield 2
    console.log(b)
    // so on...
}

const obj = back()

back.next() // { value: 1, done: false}
back.next() // { value: 2, done: true}
back.next() // { value: undefined, done: true}

Generator 后来被 async/await 取代,但是 Generator 独特的特性可以让我们在函数执行的过程中传递参数获取结果,使得函数调用变得更加灵活。

async/await

ES7 中引进的新鲜玩意,实际上 async 是一个语法糖,他的实现就是将 Generator 函数和自动执行器(co)封装在一个函数中, async/await 用过的人都知道这种跟同步代码一样的写法,条理清晰,不像 Promise 很多 then。并且他的错误可以被 try catch 捕获。

代码语言:javascript
复制
function back(data) {
    return new Promise((resolve, reject) => {
        // do something ...
        if(data) resolve(data)
        else reject(data)
    })
}
async function main() {
    // do something ...
    await back(1)
    await back(2)
    await back(3)
}

这种写法,上一条语句的代码未执行完之前下面的代码都是无法执行的。其实就类似 Promise 中 then 后面还有 then 是一样的。这种写法也是我们当前使用的比较多的处理异步的写法。

最后

都看到这里了,不点个赞再走吗?

欢迎在下方给出你的建议和留言。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021-10-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • theme: channing-cyan
  • 前言
  • 单线程的 JS
    • 进程 && 线程
      • 任务队列
        • 事件循环
        • 浏览器内核
        • 例题
          • No.1
          • 异步的历史
            • callback
              • Promise
                • Generator
                  • async/await
                  • 最后
                  相关产品与服务
                  容器服务
                  腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档