node.js 遵循了 CommonJS 的模块化规范。其中:
模块化的好处:
大家都遵守同样的模块化规范写代码,降低了沟通的成本,极大方便了各个模块之间的相互调用,利人利己
在 ES6 模块化规范诞生前,有其他如 AMD 、 CMD 、 CommonJS 模块化规范。但这些都不是官方的,还是存在一定的差异性和局限性
例如:
AMD 和 CMD 适用于浏览器端的 JavaScript 模块化
CommonJS 适用于服务器端的 JavaScript 模块化
为了降低学习难度和开发成本,统一的 ES6 模块化 就诞生了!
ES6 模块化规范中定义:
主要包含以下三种用法
语法:export default 默认导出的成员
let n1 = 10
let n2 = 20
function show () {}
// 向外共享对象
export default {
n1,
show
}
语法:import 接收名称 from ’ 模块标识符 ’
import m1 from './1. 默认导出'
console.log(m1);
只能拿到 n1 和 show
按需导出 语法:emport 按需导出的成员
export let s1 = 'aaa'
export let s2 = 'ccc'
export function say() { }
按需导入 语法:import {s1} from ’ 模块标识符 ’
import { s1, s2, say } from "./3.按需导出";
多层回调函数的相互嵌套,就形成了回调地狱。
setTimeout(() => { // 第一层
console.log('延时一秒后输出');
setTimeout(() => { //第二层
console.log('再延时两秒输出');
setTimeout(() => { //第三层
console.log('再延时三秒');
}, 3000)
}, 2000)
}, 1000);
缺点:代码耦合性强,牵一发而动全身,难以维护
为解决这个问题,提出了 Promise 的概念
① Promise 是一个构造函数
② Promise.prototype 上包含一个 .then() 方法
③ .then() 方法用来预先制定成功和失败的回调函数
调用 then-f 提供的 readFile() 方法,可以异步的读取文件的内容,它的返回值是 Promise 的实例对象。
因此可以调用 .then() 方法 为每个 Promise 异步操作制定成功和失败之后的回调函数。
import thenFs from 'then-fs'
// thenFs.readFile('./files/1.txt', 'utf8') 会拿到它的 Promise 对象
thenFs.readFile('./files/1.txt', 'utf8').then((r1) => { console.log(r1) })
thenFs.readFile('./files/2.txt', 'utf8').then((r2) => { console.log(r2) })
thenFs.readFile('./files/3.txt', 'utf8').then((r3) => { console.log(r3) })
// 当前我们只是异步的读取数据,无法保证顺序
import thenFs from 'then-fs'
// thenFs.readFile('./files/1.txt', 'utf8') 会拿到它的 Promise 对象
thenFs.readFile('./files/1.txt', 'utf8')
.then((r1) => {
console.log(r1);
return thenFs.readFile('./files/2.txt', 'utf8')
})
.then((r2) => {
console.log(r2);
return thenFs.readFile('./files/3.txt', 'utf8')
})
.then((r3) => {
console.log(r3);
})
在 Promise 的链式操作中 如果发生了错误,可以使用 Promise.prototype.catch 方法进行捕获和处理
import thenFs from 'then-fs'
// thenFs.readFile('./files/1.txt', 'utf8') 会拿到它的 Promise 对象
thenFs.readFile('./files/11.txt', 'utf8') // 没有 11.txt文件
.then((r1) => {
console.log(r1);
return thenFs.readFile('./files/2.txt', 'utf8')
})
.then((r2) => {
console.log(r2);
return thenFs.readFile('./files/3.txt', 'utf8')
})
.then((r3) => {
console.log(r3);
})
.catch((err) => {
console.log(err.message);
})
// 报错: ENOENT: No such file
Promise.all() 方法会发起并行的 Promise 异步操作,等所有的异步操作全部结束后 才会执行下一步的 .then 操作(等待机制)
import thenFs from "then-fs";
const promiseArr = [
thenFs.readFile('./files/1.txt', 'utf8'),
thenFs.readFile('./files/2.txt', 'utf8'),
thenFs.readFile('./files/3.txt', 'utf8')
]
Promise.all(promiseArr).then(result => {
console.log(result);
})
//['111','222','333']
只要任何一个异步操作完成,就立即执行下一步的 .then 操作(赛跑机制)
import thenFs from "then-fs";
const promiseArr = [
thenFs.readFile('./files/1.txt', 'utf8'),
thenFs.readFile('./files/2.txt', 'utf8'),
thenFs.readFile('./files/3.txt', 'utf8')
]
Promise.race(promiseArr).then(result => {
console.log(result);
})
// 333
方法封装的要求:
import fs from 'fs'
function getFile(fpath) {
// 如果调用 getFile 函数,就返回一个 Promise 对象
return new Promise(function (resolve, reject) {
fs.readFile(fpath, 'utf8', (err, dataStr) => {
// 如果发生错误,就把 err 传进去 reject对象
if (err) return reject(err)
// 如果上一句没有执行 说明成功了 就把 dataStr 传给 resolve
resolve(dataStr)
})
})
}
getFile('./files/1.txt').then((r1) => { console.log(r1) }, (err) => { console.log(err.message); })
async / await 是 ES8 引入的新语法,用来简化 Promise 异步操作。在此之前,开发者只能通过链式 .then() 的方法 处理 Promise 异步操作
import thenFs from 'then-fs'
async function getAllFile() {
const r1 = await thenFs.readFile('../files/1.txt', 'utf8');
console.log(r1);
// 111
}
getAllFile();
在 async 方法中,第一个 await 之前的代码会同步执行,await 之后的代码会异步执行
// 这里 A 是同步执行
console.log('A');
async function getAllFile() {
console.log('B'); // 直到这里都是 同步执行,下面碰到第一个 await,于是退出主线程并进入第 12 行代码,后面的都是异步执行
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');
JavaScript 把异步任务分为了两类,分别是:
① 宏任务
② 微任务
宏任务完成 -> 判断有微任务? -> 执行所有微任务 -> 下一个宏任务
基于 MySQL 数据库 + Express 对外提供用户列表的API接口服务。用到技术如下:
① 搭建项目的基本结构
② 创建基本的服务器
③ 创建 db 数据库操作模块
④ 创建 user_ctrl 业务模块
⑤ 创建 user_router 路由模块
① 启用 ES6 模块化支持
② 安装第三方依赖包
import express from 'express'
const app = express()
// 启用服务器,并启动在 80 端口
app.listen(80, () => {
console.log('server running at http://127.0.0.1');
})
新创建文件夹 db
import mysql from 'mysql2'
// mysql.creatPool() 的返回值 是一个数据库的连接对象
const pool = mysql.createPool({
host: '127.0.0.1', // 指定操作哪台电脑上的数据库
port: 3306, // 指定要连接的数据库的端口号
database: 'my_db_01', // 指定操作哪个数据库
user: 'root', // 登录数据库的账号
password: 'admin123' // 密码
});
// 将 pool 导出,供外界使用
export default pool.promise();
import db from '../db/index.js'
// 使用 ES6 按需导出语法
export async function getAllUser(req, res) {
const [rows] = await db.query('select id, username, nickname from ev_users')
res.send({
status: 0,
message: '获取用户列表数据成功!',
data: rows,
})
}
getAllUser()
import express from 'express'
import { getAllUser } from '../2.controller/user_ctrl.js'
// 1. 创建路由对象
const router = new express.Router();
// 3. 挂载一个路由规则
// router.get 监听客户端的 GET 请求
// 只要用户是 GET 请求,并且请求 /user 这个地址,就把这次请求,交给 getAlluser 这个函数处理
router.get('/user', getAllUser)
// 2. 将路由对象共享出去
export default router
// 4. 接下来,回到 app.js 文件中挂载
app.js:
import express from 'express'
import userRouter from './3.router/user_router.js'
const app = express()
// 增:1. 调用 app.use('') 进行挂载
// 如果用户是以 /api 方式进行访问,就指定路由模块 userRouter
app.use('/api', userRouter)
// 启用服务器,并启动在 80 端口
app.listen(80, () => {
console.log('server running at http://127.0.0.1');
})