ES6模块化与异步编程高级用法
(一)ES6模块化
ES6模块化规范是浏览器端和服务器端通用的模块化开发规范
-
- 每个js文件都是一个独立的模块
- 导入其他模块成员使用import关键字
- 向外共享模块成员使用export关键字
- ES6模块化的基本语法
- 默认导出和默认导入
// 默认导出 let n1 = 10 let n2 = 20 function show() {} export default { n1, show }
// 默认导入 import request from './默认导出js.js' console.log(request) // { n1: 10, show: [Function: show] }
-
-
- 注意事项
- 每个模块中,只允许使用唯一的一次export default
- 默认导入的时候接收名称可以是任意名称,只要是合法的成员名称即可
- 注意事项
- 按需导出和按需导入
-
// 按需导出 export let n1 = 56 export function sing(){}
// 按需导入 import { sing } from './按需导出.js' console.log(sing)//[Function: sing]
-
-
- 注意事项
- 每个模块中可以使用多次按需导出
- 按需导入的成员名称必须和按需导出的成员名称保持一致
- 按需导入时,可以使用as关键字进行重命名
- 按需导入和默认导入可以一起使用
- 注意事项
- 直接导入并执行模块化的代码
- 如果指向单纯地执行某个代码中的模块,并不需要模块中向外共享的成员,此时可以直接导入并执行模块代码
-
(二)Promise
- 回调地狱
- 多层回调函数互相嵌套,就形成了回调地狱
- Promise基本概念
- Promise是一个构造函数const p = new Promise(),new出来的Promise实例对象,代表一个异步操作
- Promise.prototype上包含着一个.then()方法
- .then()方法用来预先指定成功和失败的回调函数(成功的函数是必选的,失败的回调函数是可选的)p.then(result=>{},error=>{})
- 基于回调函数按顺序读取文件内容
- 由于node.js官方提供的fs模块仅支持以回调函数的方式读取文件,不支持Promise的调用方式。因此,要先安装then-fs这个第三方包,从而支持我们基于Promise的方式读取文件内容
- 调用then-fs提供的readFile()方法,可以异步地读取文件的内容,它的返回值是Promise实例对象。因此可以调用.then()方法为每个Promise异步操作指定成功和失败之后的回调函数
import thenFs from 'then-fs' // 下述代码无法保证文件的读取顺序 thenFs.readFile('../files/资源网站.txt','utf8').then(result=>{ console.log(result) }) thenFs.readFile('../files/软测复习.txt','utf8').then(result=>{ console.log(result) })
- then方法特性
- 如果上一个.then()方法返回了一个新的Promise实例对象,则可以通过下一个.then()继续进行处理。通过.then()方法的链式调用,就解决了回调地狱的问题
- 通过.catch捕获错误
- 在Promise的链式操作中如果发生了错误,可以使用Promise.prototype.catch方法进行捕获和处理
import thenFs from 'then-fs' // 下述代码无法保证文件的读取顺序 thenFs.readFile('../files/资源网站1.txt','utf8') .catch(()=>{ console.log("粗错啦") }) .then(result=>{ console.log(result)//undefined return thenFs.readFile('../files/软测复习.txt','utf8') }) .then(result=>{ console.log(result) })
- Promise.all()方法
- 该方法会发起并行的Promise异步操作,等所有的异步操作全部结束以后才会执行下一步的.then操作(等待机制)
import thenFs from 'then-fs' const promiseArr = [ thenFs.readFile('../files/01.txt','utf8'), thenFs.readFile('../files/02.txt','utf8'), thenFs.readFile('../files/03.txt','utf8'), ] Promise.all(promiseArr).then((r1)=>{ console.log(r1) }) //[ '我是1', '我是0', '我是0.5' ]
- Promise.race()方法
- Promise.race()方法会发起并行的Promise异步操作,只要任何一个异步操作完成,就立即执行下一步的.then操作(赛跑机制)
import thenFs from 'then-fs' const promiseArr = [ thenFs.readFile('../files/01.txt','utf8'), thenFs.readFile('../files/02.txt','utf8'), thenFs.readFile('../files/03.txt','utf8'), ] Promise.race(promiseArr).then((r1)=>{ console.log(r1) }) //[ '我是0']
- 基于Promise封装读文件的方法
- 方法的名称要定义为getFile
- 方法接收一个形参,表示要读取的文件路径
- 方法的返回值为Promise实例对象
// 基于Promise封装读文件的方法 import thenFs from 'then-fs' function getFile(fpath) { return new Promise(function(resolve,reject){ thenFs.readFile(fpath,'utf8',(err,dataStr)=>{ if(err) return reject(err) resolve(dataStr) }) }) } getFile('../files/01.txt').then(r1=>{console.log(r1)},r2=>console.log(r2))
(三)async和await
async和await是ES8引入的新语法,用来简化Promise的异步操作,再次出现之前,开发者只能通过.then()的方式处理Promise异步操作
- 基本使用
import thenFs from "then-fs" async function getAllFile(){ const r1 = await thenFs.readFile('../files/01.txt','utf8'); const r2 = thenFs.readFile('../files/02.txt','utf8'); const r3 = await thenFs.readFile('../files/03.txt','utf8'); console.log(r1)//我是1 console.log(r2)//Promise { _40: 0, _65: 1, _55: '我是0', _72: null } } getAllFile()
- 注意事项
- 如果在function中使用了await,则function必须被async所修饰
- 在async方法中,第一个await之前的代码会同步执行,await之后的代码会异步执行(先同步再异步)
(四)EventLoop
- JS是一门单线程的编程语言
- 同步任务和异步任务
- 同步任务
- 又叫做非耗时任务,指的是在主线程上排队的哪些任务
- 只有前一个任务执行完毕,才能执行后一个任务
- 异步任务
- 又叫做耗时任务 ,异步任务由JS委托给宿主环境执行
- 当异步任务执行完毕后,会通知JS主线程执行异步任务的回调函数
- 同步任务和异步任务的执行过程
- 同步任务由JS主线程按照次序执行
- 异步任务委托给宿主环境执行
- 已完成的异步任务对应的回调函数,会被加入到任务队列中等待执行
- JS主线程的执行栈被清空后,会读取任务队列中的回调函数,按照次序执行
- JS主线程不断重读上面的第4步
- Eventloop的基本概念
- JS主线程从“任务队列”中读取异步任务的回调函数,放到执行栈中依次执行。这个过程是循环不断地,所以整个的这种运行机制又被称作Eventloop(事件循环)
(五)宏任务和微任务
- 宏任务和微任务
- 宏任务
- 异步AJAX请求
- setTimeout、setInterval
- 文件操作
- 其他宏任务
- 微任务
- Promise.then、.catch和finally
- process.nextTick
- 其他微任务
- 宏任务和微任务的执行顺序(交替执行)
- 每个宏任务执行完毕之后,都会检查是否存在等待执行的微任务,如果有,则执行完所有微任务之后,再执行下一个宏任务
(六)API接口案例
基于MYSQL数据库+Express对外提供用户列表的API接口
- 创建db数据库操作模块
import mysql from "mysql2" const pool = mysql.createPool({ host: '127.0.0.1', port: 3306, database: 'my_db', user: 'root', password: '*******' }) export default pool.promise()
- 创建user_ctrl模块
import db from "../db/index.js" // 使用ES6的按需导出方法,将getAllUsers方法导出出去 export async function getAllUsers(req,res){ try{ const[rows] = await db.query('select id, username, nickname from ev_users') res.send({ status: 0, message: '获取用户列表数据成功', data: rows }) }catch(e){ res.send({ status: 1, message: '获取用户列表数据失败', desc: e.message }) } }
- 创建user_router路由模块
import express from "express" import { getAllUsers } from '../controller/user_ctrl.js' const router = new express.Router() router.get('/user', getAllUsers) export default router
- 使用postman发起get请求获取数据
http://127.0.0.1:8000/api/user
(七)总结
- 熟悉并能够熟练使用ES6的模块化语法
- 默认导出和默认导入,按需导出和按需导入
- 使用Promise解决回调地狱的问题
- promise.then()、promise.catch()
- 使用async和await简化Promise的调用
- 方法中使用了await,该方法就需要被async修饰
- Eventloop的含义
- 宏任务和微任务的执行顺序
- 在执行下一个宏任务之前,先检查是否有待执行的微任务

浙公网安备 33010602011771号