知识点合集:ES6模块、Promise、EVentLoop、宏任务与微任务

模块化相关

1.ES6模块化规范的分类

在ES6模块化规范诞生之前,javaScript社区已经尝试并提出了AMD、CMD、CommonJS等模块化规范。

但是,这些由社区提出的模块化标准,还是存在一定的差异性与局限性、并不是浏览器与服务器通过的模块化标准,例如:

  • AMD 和 CMD适合于浏览器端的Javascript模块化
  • CommonJS 适用于服务器端的javascript模块化

太多的模块化规范给开发者增加了学习的难度与开发的成本。因此,大一统的ES6模块化规范诞生了!

2.什么是ES6模块化规范

ES6模块化规范是浏览器端与服务器端通用的模块化开发规范。它的出现极大的降低了前端开发者的模块化学习成本,开发者不需要再额外学习AMD、CMD或CommonJS等模块化规范。
ES6模块化规范中定义:

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

3.在node.js中体验ES6模块化

node.js 中默认仅支持CommonJS模块化规范,若想基于node.js体验与学习ES6的模块化语法,可以按照如下两个步骤配置:
1)确保安装了v14.15.1或者更高版本的node.js
终端输入: node -V 或者 node --version
2)在pakage.json 的根节点中添加

"type":"module"   //节点

4.ES6的模块化的基本语法

ES6的模块化主要包含如下3种用法:
1)默认导出与默认导入
2)按需导出与按需导入
3)直接导入并执行模块中的代码

4.1 默认导出

默认导出的语法: export default 默认导出的成员

let n1 = 10 
let b2 = 10

function show(){

}
export default {  //使用 export default 默认导出语法,向外共享n1 和 show两个成员
  n1,
  show
}

----------------------分割线
//导入
import m1 from './xxxx.js'  //导入后m1中就拿到了导出的值

4.2 默认导出的注意事项

每个模块中,只允许使用唯一的一次export default,否则会报错!
默认导入时的接收名称可以任意名称,只要是合法的成员名称即可:

4.3 按需导出

//假设当前模块是test.js

//向外按需导出变量s1
export let s1 = 'aaa'
//向外按需导出方法
export function say(){}

4.4 按需导入

语法: import {s1} from ‘模块标识符’
当有多个成员时: import {s1,s2,say} from ‘./test.js’

4.5 按需导入和按需导出的注意事项

1)每个模块中可以使用多次按需导出
2) 按需导入的成员名称必须和按需导出的名称保持一致
3)按需导入时,可以使用as关键字进行重命名 : import {s1 as str1,s2,say} from ‘./test.js’
4)按需导入可以和默认导入一起用

export let s1 = 'aaa'
export let s2 = 'bbb'
export default {
  a:20,
}
--------------分割线
import info ,{s1,s2 as str2,say} form '模块.js'  //注意花括号内是按需导出的变量,花括号外的info则指向默认导出的变量,此时info为空

4.6 直接导入并执行模块中的代码

如果只想单纯的执行某个模块中的代码,并不需要得到模块中向外共享的成员。此时,可以直接导入并执行模块代码,示例代码如下:

//当前文件模块化为05_m3.js
//在当前模块执行一个for 循环操作
for(let i = 0;i < 3;i++){
  console.log(i)
}
-------------------分割线
//直接导入并执行模块代码,不需要得到模块向外共享的成员。此时,可以直接导入并执行模块代码,示例代码如下:
//当前文件模块为05_m3.js
//在当前模块中执行一个for循环操作
for(let i=0;i<3;i++){
  console.log(i)
}
//--------------分割线
//直接导入并执行模块代码,不需要得到模块向外共享的成员
import './05_m3.js'

Pomise相关

1.回调地狱

多层回调函数的相互嵌套,就形成了回调地狱。示例代码如下:

setTimeout(()=>{  //第一层回调函数
  console.log('延时1s后输出')
  
  setTimeout(()=>{ //第二层回调函数
    console.log('再延时两秒后输出')

    setTimeout(()=>{
      console.log('再延时3秒后输出')
    }3000)
  },2000)
},1000)

回调地狱的缺点:

  • 代码的耦合性太强,牵一发而动全身,难以维护。
  • 大量的冗余的代码互相嵌套,代码的可读性变差

1.1如何解决回调地狱

为了解决回调地狱的问题,ES6(ECMAScript 2015) 中新增了Promise的概念。

1.2 promise的基本概念

1)promise是一个构造函数

  • 我们可以创建promise示例 const p = new Promise()
  • new 出来的Promise实例对象,代表一个异步操作
    2)promise.prototype 上包含一个.then()方法
  • 每一次new Prominse() 构造函数得到的实例对象,
  • 都可以通过原型链的方式访问到.then()方法,例如p.then()
    3).then() 方法用来预先指定成功和失败的回调函数
  • p.then(成功的回调函数,失败的回调函数)
  • p.then(result=>{},error=>{})
  • 调用.then()方法时,成功的回调函数是必选的、失败的回调函数是可选的
const p = new Promise({xxx})
p.then(r1=>{
  return new promise({xxx})
})
.then(r2=>{
   return new promise({xxx})
}).catch(error=>{

})
//使用catch可以捕获错误

3. Promise.all()方法

Promise.all()方法会发起并行的Promise异步操作,等所有的异步操作全部结束后才会执行下一步的.then操作(等待机制)。示例代码如下:

//1.定义一个数组,存放3个文件的异步操作
const promiseArr = [
  thenFs.readFile('./files/11.txt','utf8'),
  thenFs.readFile('./files/22.txt','utf8'),
  thenFs.readFile('./files/33.txt','utf8')
]
// 2.将promise的数组,作为Promise.all()的参数
Promise.all(promiseArr)
.then(([r1,r2,r3])=>{
  //2.1所有文件读取成功(等待机制)
  console.log(r1,r2,r3)
})
.catch(err => {
  //2.2捕获Promise异步操作中的错误
  console.log(err.message)
})
  • 注意: 数组中Promise实例的顺序就是最终结果的顺序!

4.promise.race() 方法

promise.race() 方法会发起并行的Promise异步操作,只要任何一个异步操作完成,就立即执行下一步的.then操作(赛跑机制)。示例代码如下:

//1.定义一个数组,存放3个文件的异步操作
const promiseArr = [
  thenFs.readFile('./files/11.txt','utf8'),
  thenFs.readFile('./files/22.txt','utf8'),
  thenFs.readFile('./files/33.txt','utf8')
]
// 2.将promise的数组,作为Promise.race()的参数
Promise.race(promiseArr)
.then(result)=>{
  //2.1只要任何一个异步操作完成,就立即执行成功的回调函数(赛跑机制)
  console.log(r1,r2,r3)
})
.catch(err => {
  //2.2捕获Promise异步操作中的错误
  console.log(err.message)
})
4.1 getFile方法的基本定义
//1.方法的名称为 getFile
//2. 方法接收一个形参fpath,表示要读取的文件的路径
function getFile(fpath){
  //3.方法的返回值为 Promise的实例对象
  return new Promise()
}
  • 注意:第五行代码中的 new Promise() 只是创建了一个形式上的异步操作。
4.2创建具体的异步操作

如果想要创建具体的异步操作,则需要在new Promise() 构造函数期间,传递一个function函数,将具体的异步操作定义到function函数内部。示例代码如下:

//1.方法的名称为 getFile
//2. 方法接收一个形参fpath,表示要读取的文件的路径
function getFile(fpath){
  //3.方法的返回值为 Promise的实例对象
  return new Promise(function(){
    //4.下面这行代码,表示是一个具体的、读文件的异步操作
    fs.readFile(fpath,'utf8',(err,dataStr)=>{ })
  })
}
4.3获取.then 的两个实参
getFile('./files/1.txt').then(成功的回调函数,失败的回调函数)
4.4 调用resolve 和 reject回调函数

Promise异步操作的结果,可以调用resvole或reject回调函数进行处理。示例代码如下:

function getFile(fpath) {
  return new Promise(function(resovle,reject){
    fs.readFile(fpath,'utf8',(err,dataStr)=>{
      if(err) return reject(err)   //如果读取失败,则调用"失败的回调函数"
      resolve(dataStr)  //如果成功,则调用"成功的回调函数"
    })
  })
}
getFile('./files/1.txt').then((r1)=>console.log(r1)).catch(err=>console.log(err))

async / await

1. 什么是async/await

async/await 是ES8(ECMAScript2017) 引入的新语法,用来简化Promise异步操作。在async/await出现之前,开发者只能通过链式.then()方式处理Promise异步操作。示例代码如下:

const p = new Promise({xxx})
p.then(r1=>{
  return new promise({xxx})
})
.then(r2=>{
   return new promise({xxx})
}).catch(error=>{

})
  • .then链式调用的优点:解决了回调地狱的问题
  • .then链式调用的缺点: 代码冗余、阅读性差、不易理解

2.async/await 的基本使用

使用async/await 简化Promise异步操作的示例代码如下:

import thenFs form 'then-fs'

//按照顺序读取 1,2,3的内容
async function getAllFile(){
  const r1 = await thenFs.readFile('./files/1.txt','utf8')
  console.log(r1)
  const r2 = await thenFs.readFile('./files/2.txt','utf8')
  console.log(r2)
  const r3 = await thenFs.readFile('./files/3.txt','utf8')
  console.log(r3)
}
getAllFile()

3.async/await 使用注意事项

1)如果在function中使用了await,则function必须被async修饰
2)在async方法中,第一个await之前的代码会同步执行,await之后的代码会异步执行

console.log('A')
async function getAllFile(){
  console.log('B')
  const r1 = await thenFs.readFile('./files/1.txt','utf8')
  const r2 = await thenFs.readFile('./files/2.txt','utf8')
  const r3 = await thenFs.readFile('./files/3.txt','utf8')
  console.log(r1,r2,r3)
  console.log('D')
}
getAllFile()
console.log('C')   
//执行顺序 A B C r1 r2 r3 D

EventLoop

1.javaScript 是一门单线程的语言

javascript是一门单线程的编程语言。也就是说,同一时间只能做一件事情。
javascript的执行线程:
任务1 -------> 任务2------> 任务3 ------> 任务N
待执行的任务队列

  • 单线程执行任务队列的问题:
    如果前一个任务非常耗时,则后续的任务就不得不一直等待,从而导致程序假死的问题。

2.同步任务和异步任务

为了防止某个耗时的任务导致程序假死的问题,javascript把待执行的任务分成了两类:
1)同步任务 (synchronous)

  • 又叫做非耗时任务,指的是在主线程上排队执行的那些任务
  • 只有前一个任务执行完毕,才能执行后一个任务
    2)异步任务 (asynchrounous)
  • 又叫做耗时任务,异步任务由于javacript委托给宿主环境进行执行
  • 当异步任务执行完成后,会通知javascript主线程执行异步任务的回调函数

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

在JavaScript中,任务可以分为同步任务和异步任务。它们的执行过程有所不同:

同步任务执行过程:

  • JavaScript是单线程的,同步任务按照顺序依次执行,每个任务执行完成后才会执行下一个任务。
  • 当遇到一个同步任务时,JavaScript引擎会立即执行该任务,并阻塞后续代码的执行,直到该任务完成。
  • 同步任务执行期间,页面的渲染和用户交互都会被阻塞,直到任务执行完成。

异步任务执行过程:

  • 异步任务不会阻塞后续代码的执行,而是在后台执行,并在特定条件满足时触发回调函数的执行。
  • 当遇到一个异步任务时,JavaScript引擎会将其放入任务队列中,继续执行后续代码而不等待异步任务完成。
  • 当主线程的同步任务执行完成后,JavaScript引擎会检查任务队列,如果有异步任务已经完成,就将其对应的回调函数放入微任务队列或宏任务队列中,等待执行。
  • 微任务队列中的任务会在当前任务执行完成后立即执行,而宏任务队列中的任务则需要等待下一次事件循环开始时执行。
  • 当执行微任务队列中的任务时,如果又产生了新的微任务,会继续添加到微任务队列中,直到微任务队列为空。
  • 当执行宏任务队列中的任务时,如果又产生了新的宏任务,会添加到宏任务队列的末尾,等待下一次事件循环开始时执行。

通过这种方式,JavaScript可以处理异步操作,避免阻塞页面渲染和用户交互。常见的异步任务包括定时器回调、网络请求、事件处理等。

宏任务和微任务

1.什么是宏任务和微任务

javascript把异步任务又做了进一步划分,异步任务又分为两类,分别是:
1)宏任务(macrotask)

  • 异步Ajax请求
  • setTimeout 、setInterval、
  • 文件操作
  • 其他宏任务

2)微任务 (microtask)

在JavaScript中,微任务是指在当前任务执行完成后立即执行的任务。它们通常用于处理异步操作的回调函数。以下是一些与JavaScript微任务相关的内容:

  1. Promise:Promise是JavaScript中处理异步操作的一种机制。通过Promise对象,可以将异步操作封装成一个任务,并在任务完成后执行相应的回调函数。Promise的回调函数会作为微任务在当前任务执行完成后立即执行。
  2. async/await:async/await是ES2017引入的一种语法糖,用于更方便地处理异步操作。通过async函数和await关键字,可以以同步的方式编写异步代码。在使用await关键字等待一个Promise对象时,后续代码会被封装成微任务,在当前任务执行完成后立即执行。
  3. MutationObserver:MutationObserver是一个用于监听DOM变化的API。它可以观察DOM树的变化,并在变化发生后执行回调函数。MutationObserver的回调函数会作为微任务在当前任务执行完成后立即执行。
  4. queueMicrotask():queueMicrotask()是一个用于将任务添加到微任务队列的函数。它接受一个回调函数作为参数,并将该回调函数添加到微任务队列中。添加的任务会在当前任务执行完成后立即执行。

这些是JavaScript中与微任务相关的一些内容。通过使用这些机制和API,可以更好地处理异步操作,并在需要时执行相应的回调函数。

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

每一个宏任务执行完成之后,都会检查是否存在待执行的微任务,如果有,则执行完所有的微任务之后,再继续执行下一个宏任务。

宏任务和微任务都是指异步任务的执行方式。

宏任务指的是由浏览器或Node.js环境提供的异步任务,比如setTimeout、setInterval、I/O操作等。当宏任务被加入到任务队列中后,等待JavaScript引擎的执行。

微任务指的是由JavaScript引擎提供的异步任务,比如Promise、MutationObserver等。当微任务被加入到任务队列中后,会在当前宏任务执行完毕后立即执行。

执行顺序:当一个宏任务执行完毕后,JavaScript引擎会先执行所有微任务,然后再执行下一个宏任务。这意味着在同一个宏任务中,微任务总是优先于下一个宏任务执行。而不同宏任务之间的执行顺序是不确定的,取决于它们被加入到任务队列中的先后顺序。

当前任务
在JavaScript中,"当前任务"指的是当前正在执行的任务或代码块。JavaScript是单线程的,一次只能执行一个任务。任务可以是同步任务或异步任务。
同步任务是按照顺序依次执行的任务,每个任务执行完成后才会执行下一个任务。例如,函数调用、循环、条件语句等都是同步任务。
异步任务是在后台执行的任务,不会阻塞后续代码的执行。当遇到异步任务时,JavaScript引擎会将其放入任务队列中,并继续执行后续代码。异步任务的执行通常是由事件触发、定时器到期、网络请求返回等条件触发的。当主线程的同步任务执行完成后,JavaScript引擎会检查任务队列,如果有已完成的异步任务,就将其对应的回调函数放入微任务队列或宏任务队列中,等待执行。
因此,当前任务可以是正在执行的同步任务,也可以是正在等待执行的异步任务。微任务队列中的任务会在当前任务执行完成后立即执行,而宏任务队列中的任务则需要等待下一次事件循环开始时执行。
下一次事件循环
下一次事件循环是指在JavaScript中,当当前任务执行完成后,JavaScript引擎开始执行下一个任务的过程。事件循环是JavaScript的执行模型,用于处理异步任务和事件回调。
事件循环的过程包括以下几个步骤:

  1. 执行当前任务:JavaScript引擎执行当前任务,这可以是同步任务或异步任务。
  2. 检查微任务队列:在当前任务执行完成后,JavaScript引擎会立即检查微任务队列。如果微任务队列中有任务,引擎会按照队列中的顺序依次执行这些微任务。
  3. 更新渲染:如果当前任务对页面进行了修改,JavaScript引擎会通知浏览器进行页面的重新渲染。
  4. 检查宏任务队列:在微任务执行完毕后,JavaScript引擎会检查宏任务队列。如果宏任务队列中有任务,引擎会选择其中的一个任务执行。
  5. 执行宏任务:JavaScript引擎执行选中的宏任务,这可以是定时器回调、事件回调、网络请求回调等。
  6. 回到步骤2:在宏任务执行完成后,JavaScript引擎会回到步骤2,继续检查微任务队列并执行微任务,然后再次检查宏任务队列。

这个过程会不断循环,直到微任务队列和宏任务队列都为空。这样可以确保异步任务和事件回调按照正确的顺序执行,并且在每个任务执行之间允许浏览器进行页面渲染和用户交互响应。

3.分析以下代码输出的顺序

setTimeout(function(){
  console.log('1')
})
new Promise(function(resolve){
  console.log('2')
  resolve()
}).then(function(){
  console.log('3')
})
console.log('4')    
//执行顺序 2 4 3 1

分析:
1)先执行所有的同步任务,new Promise是个同步任务,new完后里面的function会立即执行 所以先输出2,然后同步任务4

2)再执行微任务
promise.then里面就是个微任务 所以输出3

3)再执行宏任务
setTimeout 输出 1

4.经典面试题

console.log('1')
setTimeout(function(){
  console.log('2') //
  new Promise(function(resolve){
    console.log('3')//
    resolve()
  }).then(function(){
    console.log('4') //
  })
})
new Promise(function(resolve){
  console.log('5')
  resolve()
}).then(function(){
  console.log('6')
})
setTimeout(function(){
  console.log('7')//
  new Promise(function(resolve){
    console.log('8') //
    resolve()
  }).then(function(){
    console.log('9') //
  })
})

以上代码输出结果: 1 5 6 2 3 4 7 8 9

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

godlike-icy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值