ES6模块化与异步编程

1. ES6模块化

1.1 前端模块化规范的分类

ES6模块化规范诞生之前,js社区尝试提出AMD、CMD、CommonJS等模块化规范,但不在浏览器和服务器端通用,故产生了ES6模块化规范。

1.2 什么是ES6模块化规范?

ES6模块化规范是浏览器端与服务器端通用的模块化开发规范。ES6模块化规范中定义:

  • 每个js文件是一个独立的模块
  • 导入其他模块成员使用import关键字
  • 向外共享模块成员使用export关键字

1.3 ES6模块化中的默认导出与默认导入

  • 默认导出: export default 默认导出的成员
  • 默认导入:import 接收名称(一个对象) from ‘模块标识符’

1.4 ES6模块化中的按需导出与按需导入

  • 按需导出:export 按需导出的成员

  • 按需导入:import {xxx} from ‘模块标识符’


默认v.s.按需

  • 默认导出只能用一次,按需导出可以使用多次
  • 导入默认导出成员可自定义名字,而导入按需导出的成员必须按照{按需导出成员名字(as xx)}的格式

2. Promise

2.1 回调地狱的问题

回调地狱:多层回调函数的相互嵌套就会形成回调地狱,难以理解和维护。

e.g. 基于回调函数的按顺序读取文件内容的代码

回调地狱

回调地狱的问题在于其使得代码难以扩展、理解和调试。

为了解决回调地狱的问题,ES6新增了Promise的概念。

2.2 Promise的基本概念

Promise是js中用于处理异步操作的对象,它代表了一个尚未完成但最终会完成的操作

  • Promise是一个构造函数:

    const p = new Promise( (resolve, reject) => { // p是一个Promise的实例对象,代表一个异步操作

    ​ // 异步操作

    ​ // 如果操作成功,调用 resolve(value)

    ​ // 如果操作失败,调用 reject(error) })

  • Promise.prototype上包含then()方法,用于预先指定成功和失败的回调函数

    p.then(

    ​ (result) => {

    ​ //处理操作成功的情况},

    ​ (error) => {

    ​ //处理操作失败的情况}

    )


在2.1中展示的读取文件操作是基于node.js官方提供的fs模块,但是fs仅支持以回调函数的方式读取文件,不支持Promise调用方式。

安装then-fs第三方包

调用then-fs的readFile()方法可以异步读取文件的内容,返回值是一个Promise实例对象,再调用.then()方法为每一个异步操作指定成功和失败的回调函数。

e.g. 基于Promise的调用方式(不完善)

基于Promise的调用方式1

问题: 一次性开启三个异步操作去读文件,哪个先读取完成是位置的,无法保证读取顺序

问题

链式调用: Promise对象的then()方法可以返回一个新的Promise对象,使得可以使用链式调用来处理多个异步操作。这种方式可以避免回调地狱,使代码更加清晰。

Promise链式调用

那么错误如何捕获?

通过catch捕获和处理错误

catch

如果不希望前面的错误导致后续的.then()无法正常执行,可以将catch的调用提前。

2.3 Promise.all()方法

Promise.all()能够一次并行处理多个Promise异步操作,等所有的异步操作全部结束后才会执行下一步的.then()操作,也就是一个等待机制

Promise.all

2.4 Promise.race()方法

Promise.race()方法会发起并行的Promise异步操作,只要任何一个异步操作完成,就会立即执行下一步的.then()操作,这里是赛跑机制,顺序不定!

Promise.race

3. async / await

3.1 什么是async / await?

async / await是ES8引入的语法,为了简化Promise异步操作

在之前,我们只能通过链式.then()的方式处理Promise异步操作

虽然解决了回调地域的问题,但是代码仍然可读性差、不易理解

3.2 async / await基本使用

  • async: async是一个加在函数前的修饰符,被async定义的函数会默认返回一个Promise对象resolve的值。
  • await: async修饰的函数本来应该返回一个Promise实例对象,加上await就会返回读取结果。

简化Promise异步操作


注:具体的Promise&async/await可以参考:

https://javascript.info/async

4. EventLoop

4.1 单线程语言

Js是一门单线程执行的编程语言。也就是说若前一个任务十分耗时,则后续任务都需要一致等待,导致程序假死的现象。

4.2 同步任务和异步任务的概念

为了防止上述的程序假死问题,js把待执行的任务分成了两类:

  • 同步任务非耗时任务,在主线程上排队执行的任务。特点就是前一个执行完毕才会执行后一个。
  • 异步任务耗时任务,由js委托给宿主环境进行执行。特点是当异步任务执行完成后,会通知js主线程执行异步任务的回调函数。

4.3 同步任务和异步任务的执行过程

同步任务和异步任务的执行过程

  • 同步任务由js主线程按次序执行
  • 异步任务委托给宿主环境执行
  • 已完成的异步任务对应的回调函数,会被加入到任务队列中等待
  • js主线程的执行栈被清空后,会读取任务队列中的回调函数,按次序执行
  • js主线程不断重复上面的步骤

4.4 EventLoop的基本概念

js主线程从“任务队列”中读取异步任务的回调函数并放到执行栈中依次执行。这过程是循环不断的,整个运行机制又称为EventLoop。

5. 宏任务和微任务

js又将异步任务更一步划分为:宏任务和微任务

5.1 什么是宏任务和微任务?

  • 宏任务: macrotask,通常是一些比较耗时的操作
    • 异步Ajax请求
    • setTimeout、setInterval
    • 文件操作
    • 其他宏任务
  • 微任务: macrotask,通常是一些轻量级的任务
    • Promise.then()、catch()
    • process.nextTick
    • 其他微任务

5.2 宏任务和微任务的执行顺序

每一个宏任务执行完都会检查是否存在待执行的微任务

宏任务和微任务的执行顺序


事件循环的执行顺序:

  1. 执行当前的同步任务。
  2. 检查微任务队列,按顺序执行所有微任务,直到微任务队列为空。
  3. 更新 UI 渲染(如果需要)。
  4. 执行下一个宏任务,从宏任务队列中取出一个宏任务执行。
  5. 回到步骤 2,继续执行微任务和宏任务,直到所有任务完成。

经典面试题:

题

ans: 1,5,6,2,3,4,7,8,9


ES6模块化与异步编程
http://example.com/2023/07/17/ES6模块化与异步编程/
Author
John Doe
Posted on
July 17, 2023
Licensed under