nodeES6模块化 + Promise(ES6新对象) +async和await(ES2017关键字) 5+JS执行机制(事件循环)

node学习总结2

  1. nodeES6模块化
  2. Promise(ES6新对象)
  3. async和await(ES2017关键字)
  4. async和await读取JSON文件案例
  5. JS执行机制(事件循环)
  6. 微任务和宏任务

1. ES6模块化

1.1 CommonJS 规范

node.js 遵循了 CommonJS 的模块化规范。其中:

  • 导入其它模块使用 require()方法
  • 模块对外共享成员使用 module.exports 对象

1.2 模块化开发好处

模块化开发的好处有很多,其中:

  • 实现了在JS文件中引用其他文件
  • 避免命名冲突的问题
  • 大家都遵守同样的模块化规范写代码,降低了沟通的成本,极大方便了各个模块之间的相互调用
  • 只需关心当前模块本身的功能开发,需要其他模块的支持时,在模块内调用目标模块即可

1.3 模块化规范划分

在 ES6 模块化规范诞生之前,JavaScript 社区已经尝试并提出了 AMD 、 CMD 、 CommonJS 等模块化规范。
但是,这些由社区提出的模块化标准,还是存在一定的差异性与局限性、并不是浏览器与服务器通用的模块化标准

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

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

1.4 ES6模块化开发注意点

node.js 中默认仅支持 CommonJS 模块化规范,若想基于 node.js 体验与学习 ES6 的模块化语法,需要按照如下两个步骤进行配置:

  • 确保安装了 v13.0.0 或更高版本的 node.js
  • 在 package.json 的根节点中添加 “type”: “module” 节点
  • 配置之后,则只能使用ES6模块化语法,不能再使用CommonJS语法了

1.5 ES6 导入导出

三种方式:1.导入导出缺省的对象.2.按需导入导出2.仅导入导出逻辑

a.js文件:

// // 创建文件夹: 注意别有特殊符号和汉字
// // 初始化:  npm  init   -y

// // 导入方法1 - 前缀后缀不能省略;
// // default导出的数据,不能解构;
// import  obj  from  './02-b.js';
// console.log(obj);
// obj.c();

// 导入方法2 - 前缀后缀不能省略;
// 按需导出的数据,必须解构导入
import  { d, e, f as fn }  from  './02-b.js';
console.log(d, e);
fn();

// // 导入方法3 - 只导入,不接收;
// import './02-b.js'

b.js文件:

// // 导出方法1
// // default导出,不能解构;
// export  default  {
//     a: 1,
//     b: 2,
//     c: () => console.log('我是函数c')
// }

// 导出方法2 - 按需导出
//   必须以解构的方式接收
export  let  d = 3;
export  const  e = 4;
export  function f() {
    console.log('我是函数f');
} 

// // 第三种导出,一般使用的是逻辑,不是导出的具体值
// console.log('忽有故人人上过,回首山河已是秋。他朝若是同淋雪,此生也算共白头。');

2.Promise对象

​ 异步操作是 JavaScript 编程的麻烦事,麻烦到一直有人提出各种各样的方案,试图解决这个问题。早期使用回调函数处理异步编码,但存在回调地狱的问题。ES6中,新增了Promise 对象,从此异步编程摆脱了回调函数的束缚。

2.1 使用语法

let p = new Promise((resolve, reject) => {
    // ... some code
    if (/* 异步操作成功 */) {
        resolve(value);
    } else {
        reject(error);
    }
});

注意点:

  • new Promise()
  • 必须传入一个函数作为Promise的参数,这个函数在 new Promise的时候就会执行
  • 函数有 resolve 和 reject 两个形参
  • 函数就相当于一个容器,可以将异步任务放到这里
  • 将异步任务成功的结果传给 resolve 函数;将失败的信息传给 reject 函数
p.then(
    result => { /* 获取成功的结果 */ }
);
// 或者
p.then(
    result => { /* 获取成功的结果 */ },
    err => { /* 获取失败的结果 */ }
);
// 或者
p.then(
    result => { /* 获取成功的结果 */ }
).catch(
	err => { /* 获取失败的结果 */ }
);

注意点:

  • then方法接收以个函数类型的参数,只处理成功
  • then方法接收两个函数类型的参数,分别用于接收 resolve 的值 和 reject 的值
  • then方法也可以只接收一个参数,表示只接收 resolve 的值,失败的结果可以通过链式调用catch方法捕获

2.2 Promise中的同步异步

new Promise 和 new 其他对象一样,是同步任务。

获取结果时(调用 resolve 触发 then方法时)是异步的。

2.3 使用Promise解决回调地狱

代码略;

2.4 Promise 封装

// 封装
function myReadFile(filename) {
    return new Promise((resolve, reject) => {
        fs.readFile(filename, 'utf-8', (err, data) => {
            err ? reject(err) : resolve(data.length);
        })
    });
}
// 调用
myReadFile('./files/a.txt')
    .then(a => {
        console.log(a);
        return myReadFile('./files/b.txt');
    })
    .then(b => {
        console.log(b);
        return myReadFile('./files/c.txt');
    })
    .then(c => {
        console.log(c)
    })

2.4 第三方then-fs解决回调地狱

// npm  i  then-fs
const fs = require('then-fs');
fs.readFile('./files/a.txt', 'utf-8')
    .then(res1 => {
        console.log(res1);
        return fs.readFile('./files/b.txt', 'utf-8')
    })
    .then(res2 => {
        console.log(res2);
        return fs.readFile('./files/b.txt', 'utf-8')
    })
    .then(res3 => {
        console.log(res3)
    })

注意:未来很多模块支持Promise对象开发,就是返回的是一个Promise对象; 如 axios

3. async 和 await 关键字

​ async 和 await 是 ES2017 中提出来的,async 和 await 两个关键字的出现,简化的 Promise 的使用。

3.1 async 关键字

async关键字使用比较简单,所以 async 的使用注意以下三点即可 :

  1. async 用于修饰一个 function
  2. async 修饰的函数,总是返回一个 Promise 对象
  3. 函数内的所有值,将自动包装在 resolved 的 promise 中

3.2 await关键字

await关键字比较繁琐,注意点比较多。首先,await 只能出现在 async 函数内,await 让 JS 引擎等待直到promise完成并返回结果,语法:

​ let value = await promise对象; // 等待promise对象的结果,然后将结果赋值给 value

由于await需要等待promise执行完毕,所以 await会 暂停函数的执行,但不会影响其他同步任务。

所以,await总结如下四点:

  1. await只能出现在异步函数中!
  2. await能停止代码执行,让后面的同步代码,先执行;
  3. await后面跟随的是一个promise对象;
  4. await返回的是: Promise对象中的then()中的回调函数中的参数res;

3.3 async 和 await 解决回调地狱

// async 和 await 解决回调地狱也要用到 then-fs ,因为他直接返回 Promise 对象;

// 导入 then-fs
import thenFs from 'then-fs';

// await 一定要出现在异步函数中
async function fn() {
    let str1 = await thenFs.readFile('./txt/a.txt', 'utf8');
    console.log(str1);
    let str2 = await thenFs.readFile('./txt/b.txt', 'utf8');
    console.log(str2);
    let str3 = await thenFs.readFile('./txt/c.txt', 'utf8');
    console.log(str3);
}

// 调用函数
fn();

5.JS执行机制(事件循环)

nodeES6模块化 + Promise(ES6新对象) +async和await(ES2017关键字) 5+JS执行机制(事件循环)

  1. js代码开始执行后,主线程执行栈中会把任务分为两类.
  2. 一类是同步任务, 一类是异步任务; 主线程执行栈优先执行同步任务,
  3. 异步任务会被放入特定的处理程序中,满足条件后,被放到消息(任务/事件)队列中,
  4. 主线程执行栈中所有的同步任务执行完毕之后,通过事件循环去消息(任务/事件)队列中,
  5. 挑选优先满足条件的程序,放入主线程执行栈中执行。事件循环,周而复始,一直执行。

6.微任务和宏任务

​ 在ES3 以及以前的版本中,JavaScript本身没有发起异步请求的能力,也就没有微任务的存在。在ES5之后,JavaScript引入了Promise,这样,不需要浏览器,JavaScript引擎自身也能够发起异步任务了。
​ Tick会触发浏览器渲染,Promise不会触发,所以更加轻量级,多使用;

6.1 宏任务

(macro)task,可以理解是每次执行栈执行的代码就是一个宏任务

  • 主线程上的执行栈中所有的代码块
  • setTimeout
  • setInterval
  • Ajax
  • 事件

6.2 微任务

微任务(microtask)是宏任务中的一个部分,它的执行时机是在同步代码执行之后,下一个宏任务执行之前。总结起来,微任务有:

  • Promise.then
  • process.nextTick(Node.js 环境)

6.3 宏任务和微任务执行机制

JS优先执行同步任务,然后执行微任务,最后执行宏任务。

总结:Promise中的then()比大部分异步代码,优先执行!

上一篇:maven导自定义包失败的问题


下一篇:2022-2023年英语周报七年级第37期答案汇总