(一)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的含义
- 宏任务和微任务的执行顺序
- 在执行下一个宏任务之前,先检查是否有待执行的微任务