目录
ES6模块化
模块化介绍
1.在node.js中默认的模块化
2.ES6模块化规范
3.ES6模块的规范定义
4.在node.js中使用ES6模块化(在 package.json 的根节点中添加 "type": "module" 节点)
ES6模块化的使用
1.默认导出(export default 默认导出的成员)
2.默认导入(import 接收名称 from '模块标识符)
3.按需导出 (export 按需导出的成员)
4.按需导入(import { s1 } from '模块标识符')
5.直接导入并执行模块中的代码
Promise
Promise 解决的问题
1.回调地狱
2.Promise的基本概念
基于then-fs第三方包读取文件内容(支持Promise调用方法)
1.then-fs 的基本使用(readFile()方法异步读取文件,返回Promise实例对象)
2.then() 方法的特性(链式调用解决回调地狱问题)
3.通过 .catch 捕获错误
4.基于 Promise 按顺序读取文件的内容
5.Promise.all() 方法(等待机制:数组中 Promise 实例的顺序, 就是最终结果的顺序)
6.Promise.race() 方法(赛跑机制:谁执行快就拿谁作为结果)
基于 Promise 封装读文件的方法
1 getFile 方法的基本定义和创建具体的异步 *** 作
2.获取 .then 的两个实参和调用 resolve 和 reject 回调函数
async/await(ES8)
1.async/await的基本使用
EventLoop(事件循环)
1. JavaScript 是单线程的语言
2. 同步任务和异步任务
3. 同步任务和异步任务的执行过程
4.JS执行机制
5.结合 EventLoop 分析输出的顺序
宏任务和微任务
1.宏任务和微任务的执行顺序
2.宏任务和微任务顺序案例
API接口案例(Promise应用)
实现步骤
1.搭建项目的基本结构
2.创建最基本的服务器(导入,创建实例,指定端口启动web服务器)
3.创建 db 数据库 *** 作模块
4.创建user_ctrl模块
5.创建API路由模块和在app.js进行导入注册
ES6模块化 模块化介绍 1.在node.js中默认的模块化 node.js 遵循了 CommonJS 的模块化规范。其中:
- 导入其它模块使用 require() 方法
- 模块对外共享成员使用 module.exports 对象
- AMD 和 CMD 适用于浏览器端的 Javascript 模块化
- CommonJS 适用于服务器端的 Javascript 模块化
- 每个 js 文件都是一个独立的模块
- 导入其它模块成员使用 import 关键字
- 向外共享模块成员使用 export 关键字
- ① 确保安装了 v14.15.1 或更高版本的 node.js
- ② 在 package.json 的根节点中添加 "type": "module" 节点
let n1=10
let n2=20
function show(){}
/* 每个模块中,只允许使用唯一一次的默认导出export default */
export default{
n1,
show
}
2.默认导入(import 接收名称 from '模块标识符)
默认导入的语法:
import
接收名称
from
'
模块标识符
默认导入时的接收名称可以任意名称,只要是合法的成员名称即可:
import m1 from './01.默认导出.js'
console.log(m1);
3.按需导出 (export 按需导出的成员)
按需导出的语法:
export
按需导出的成员
每个模块中可以使用多次按需导出
//按需导出,可以导出多次
export let s1='dilireba'
export let s2='ccc'
export function say(){}
export default{
a:20
}
4.按需导入(import { s1 } from '模块标识符')
按需导入的语法:
import
{
s1
}
from
'模块标识符'
- 按需导入的成员名称必须和按需导出的名称保持一致
- 按需导入时,可以使用 as 关键字进行重命名
- 按需导入可以和默认导入一起使用
// 按需导入,名称必须跟按需导出的名称一样
//可以通过 as 进行重命名
//按需导入可以和默认导入一起用
import info,{ s1,s2 as str2,say } from './03.按需导出.js'
console.log(s1);
console.log(str2);
console.log(say);
console.log(info);
5.直接导入并执行模块中的代码
如果
只想单纯地执行某个模块中的代码
,并不需要得到模块中向外共享的成员。此时,可以直接导入并执行模块代码
import './05.直接运行模块中的代码.js'
05.文件中:
for(let i=0;i<3;i++){
console.log(i);
}
Promise
Promise 解决的问题
1.回调地狱
多层回调函数的相互嵌套
,就形成了
回调地狱
。
回调地狱的缺点:
- 代码耦合性太强,牵一发而动全身,难以维护
- 大量冗余的代码相互嵌套,代码的可读性变差
- 我们可以创建 Promise 的实例 const p = new Promise()
- new 出来的 Promise 实例对象,代表一个异步 *** 作
- 每一次 new Promise() 构造函数得到的实例对象,
- 都可以通过原型链的方式访问到 .then() 方法,例如 p.then()
- p.then(成功的回调函数,失败的回调函数)
- p.then(result => { }, error => { })
- 调用 .then() 方法时,成功的回调函数是必选的、失败的回调函数是可选的
基于then-fs第三方包读取文件内容(支持Promise调用方法) 由于 node.js 官方提供的 fs 模块 仅支持 以 回调函数的方式 读取文件, 不支持 Promise 的调用方式 。因此,需要先运行如下的命令,安装 then-fs 这个第三方包,从而支持我们基于 Promise 的方式读取文件的内容
装包:npm i then-fs
1.then-fs 的基本使用(readFile()方法异步读取文件,返回Promise实例对象) 调用 then-fs 提供的 readFile() 方法,可以异步地读取文件的内容, 它的返回值是 Promise 的实例对象 。因 此可以 调用 .then() 方法 为每个 Promise 异步 *** 作指定 成功 和 失败 之后的回调函数。import thenFs from 'then-fs'
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);})
/* 因为上面都是异步 *** 作,无法保证文件的读取顺序 */
2.then() 方法的特性(链式调用解决回调地狱问题)
如果上一个 .then() 方法中
返回了一个新的 Promise 实例对象
,则可以通过下一个 .then() 继续进行处理。通过 .then() 方法的
链式调用
,就解决了回调地狱的问题
3.通过 .catch 捕获错误
在 Promise 的链式 *** 作中如果发生了错误,可以使用 Promise.prototype.
catch
方法进行捕获和处理
4.基于 Promise 按顺序读取文件的内容
Promise 支持链式调用,从而来解决回调地狱的问题
import thenFs from 'then-fs'
/* 调用 then-fs 提供的 readFile() 方法,可以异步地读取文件的内容
,它的返回值是 Promise 的实例对象。 */
// 1.在 Promise 的链式 *** 作中如果发生了错误,
//可以使用 Promise.prototype.catch 方法
// 2.如果不希望前面的错误导致后续的
// .then 无法正常执行,则可以将 .catch 的调用提前
thenFs.readFile('./files/11.txt','utf8')
.catch((err)=>{
console.log(err.message);
}).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);
})
5.Promise.all() 方法(等待机制:数组中 Promise 实例的顺序, 就是最终结果的顺序)
Promise.all() 方法会发起并行的 Promise 异步 *** 作,等
所有的异步 *** 作全部结束后
才会执行下一步的 .then *** 作(等待机制)。
import thenFs from 'then-fs'
/* Promise.all() 方法会发起并行的 Promise 异步 *** 作,
等所有的异步 *** 作全部结束后才会执行下一步的 .then
*** 作(等待机制)。 */
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);
})
注意:数组中 Promise 实例的顺序, 就是最终结果的顺序
6.Promise.race() 方法(赛跑机制:谁执行快就拿谁作为结果) Promise.race() 方法会发起并行的 Promise 异步 *** 作, 只要任何一个异步 *** 作完成,就立即执行下一步的 .then *** 作 (赛跑机制)。import thenFs from 'then-fs'
/* Promise.race() 方法会发起并行的 Promise 异步 *** 作,
只要任何一个异步 *** 作完成,就立即执行下一步的
.then *** 作(赛跑机制)。谁执行快就拿谁作为结果 */
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);
})
基于 Promise 封装读文件的方法
方法的封装要求:
- ① 方法的名称要定义为 getFile
- ② 方法接收一个形参 fpath,表示要读取的文件的路径
- ③ 方法的返回值为 Promise 实例对象
具体步骤:
1 getFile 方法的基本定义和创建具体的异步 *** 作//方法接收一个形参fpath,表示要读取的文件路径
function getFile(fpath){
//返回的是一个Promise实例对象
return new Promise()
}
这时new Promise()只是创建了一个形式上的异步 *** 作,如果想要创建具体的异步 *** 作,则需要在 new Promise() 构造函数期间,传递一个 function 函数,将具体的异步 *** 作定义到 function 函数内部。
function getFile(fpath){
return new Promise(function(){
//下面的代码表示这是一个读文件的异步 *** 作
fs.readFile(fpath,'utf8',(err,dataStr)=>{ })
})
}
2.获取 .then 的两个实参和调用 resolve 和 reject 回调函数
通过 .then() 指定的
成功
和
失败
的回调函数,可以在 function 的
形参中
进行接收,
Promise
异步 *** 作的结果
,可以调用
resolve
或
reject
回调函数进行处理。
/* 基于Promise封装异步读文件的方法 */
import fs from 'fs'
/* resolve 是成功的回调函数,reject是失败的回调函数 */
/* err 是读取失败返回的结果,dataStr是读取成功返回的结果 */
function getFile(fpath){
return new Promise(function(resolve,reject){
fs.readFile(fpath,'utf8',(err,dataStr)=>{
/* 如果读取失败。则调用失败回调函数 */
if(err) return reject(err)
/* 如果读取成功,则调用成功回调函数 */
resolve(dataStr)
})
})
}
/* Promise 异步 *** 作的结果,可以调用 resolve 或 reject 回调函数进行处理 */
/* 方法测试 */
/* 通过 .then() 指定的成功和失败的回调函数,可以在 function 的形参中进行接收 */
getFile('./files/1.txt').then((r1)=>{
console.log(r1);
},(err)=>{
console.log(err.message);
})
//也可以通过catch捕获异常
getFile('./files/11.txt').then((r1)=>{
console.log(r1);
}).catch((err)=>console.log(err.message))
async/await(ES8)
async/await
是
ES8
(ECMAScript 2017)引入的新语法,用来
简化 Promise 异步 *** 作
。在 async/await 出现之前,开发者只能通过
链式 .then() 的方式处理 Promise 异步 *** 作。
- .then 链式调用的优点: 解决了回调地狱的问题
- .then 链式调用的缺点: 代码冗余、阅读性差、不易理解
import thenFs from 'then-fs'
/* async/await 是 ES8(ECMAScript 2017)引入的新语法,
用来简化 Promise 异步 *** 作。在 async/await 出现之前,
开发者只能通过链式 .then() 的方式处理 Promise 异步 *** 作 */
/* 如果方法的返回值是个Promise对象,可以用await修饰
这样就可以拿到读取到的值,r1不再是Promise对象,而是读取到的数据
注意:如果用了await(等待)进行修饰,则需要在方法前加async(异步)关键字进行修饰 */
console.log('A');
async function getAllFile(){
console.log('B');
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);
console.log('D');
}
/* 在 async 方法中,第一个 await 之前的代码会同步执行,await 之后的代码会异步执行 */
getAllFile()
console.log('C');
如果方法的返回值是个Promise对象,可以用await修饰,这样就可以拿到读取到的值,r1不再是Promise对象,而是读取到的数据。
注意:
- 如果用了await(等待)进行修饰,则需要在方法前加async(异步)关键字进行修饰
- 在 async 方法中,第一个 await 之前的代码会同步执行,await 之后的代码会异步执行
- 又叫做非耗时任务,指的是在主线程上排队执行的那些任务,同步任务都在主线程上执行,形成一个执行栈。
- 只有前一个任务执行完毕,才能执行后一个任务
- 又叫做耗时任务,异步任务由 JavaScript 委托给宿主环境进行执行
- 当异步任务执行完成后,会通知 JavaScript 主线程执行异步任务的回调函数
- 1、普通事件,如 click、resize 等
- 2、资源加载,如 load、error 等
- 3、定时器,包括 setInterval、setTimeout 等
- 异步任务相关回调函数添加到任务队列中(任务队列也称为消息队列)。
- ① 同步任务由 JavaScript 主线程次序执行
- ② 异步任务委托给宿主环境执行
- ③ 已完成的异步任务对应的回调函数,会被加入到任务队列中等待执行
- ④ JavaScript 主线程的执行栈被清空后,会读取任务队列中的回调函数,次序执行
- ⑤ JavaScript 主线程不断重复上面的第 4 步
由于主线程不断的重复获得任务、执行任务、再获取任务、再执行,所以这种机制被称为事件循环( event loop)。
4.JS执行机制1. 先执行执行栈中的同步任务。
2. 异步任务(回调函数)放入任务队列中。
3. 一旦执行栈中的所有同步任务执行完毕,系统就会按次序读取任务队列中的异步任务,于是被读取的异步任务结束等待状态,进入执行栈,开始执行。
5.结合 EventLoop 分析输出的顺序import thenFs from 'then-fs'
console.log('A');
thenFs.readFile('./files/1.txt','utf8')
.then(dataStr=>{
console.log('B');
})
setTimeout(()=>{
console.log('C');
},0)
console.log('D');
//结果:ADCB
/*
A和D属于同步任务
C 和 B 属于异步任务。
它们的回调函数会被加入到任务队列中,
等待主线程空闲时再执行,
C走个过程,因为设置了0毫秒
*/
宏任务和微任务
JavaScript 把异步任务又做了进一步的划分,异步任务又分为两类,分别是:
① 宏任务(macrotask)
- 异步 Ajax 请求、
- setTimeout、setInterval、
- 文件 *** 作
- 其它宏任务
- Promise.then、.catch 和 .finally
- process.nextTick
- 其它微任务
先执行同步任务,接着执行微任务,再执行第一个宏任务,
然后每一个宏任务执行完之后,都会检查是否存在待执行的微任务,
如果有,则执行完所有微任务之后,再继续执行下一个宏任务。
例如:两人去银行办业务的场景,只有一个柜员。 2.宏任务和微任务顺序案例面试题1:
/* 先执行同步任务
再执行异步任务的微任务,
最终执行异步任务的宏任务 */
/* 4.延时器是异步任务中的宏任务 */
setTimeout(function(){
console.log('1');
})
/* 1.new Promise里面的方法会立即执行,是一个同步任务 */
new Promise(function(resolve){
console.log('2');
resolve()
}).then(function(){
console.log('3');
})
/* 3.Promise的.then方法是异步任务的微任务 */
/* 2.同步任务 */
console.log('4');
//2431
面试题2:
console.log('1');//同步任务
setTimeout(function(){//宏任务1
console.log('2');//宏任务1中的第一个任务
new Promise(function(resolve){
console.log('3');
resolve()//宏任务1中的第二个任务
}).then(function(){
console.log('4');//宏任务1中的微任务
})
})
new Promise(function(resolve){
console.log('5');
resolve()//同步任务
}).then(function(){
console.log('6');//微任务
})
setTimeout(function(){//宏任务2
console.log('7');//宏任务2的第一个任务
new Promise(function(resolve){
console.log('8');//宏任务2的第二个任务
resolve()
}).then(function(){宏任务2的微任务
console.log('9');
})
})
//156234789
总结:先执行同步任务,
注意new Promise()function中立即调用属于同步任务,
然后检查在这个级别下有无微任务,如果有就执行微任务,
然后再执行第一个宏任务,第一个宏任务中的任务执行完,
检查该宏任务有无微任务,如果有就执行,再执行下一个宏任务,重复这样 *** 作。
API接口案例(Promise应用) 基于 MySQL 数据库 + Express 对外提供 用户列表 的 API 接口服务。用到的技术点如下:- 第三方包 express 和 mysql2
- ES6 模块化
- Promise
- async/await
- ① 搭建项目的基本结构
- ② 创建基本的服务器
- ③ 创建 db 数据库 *** 作模块
- ④ 创建 user_ctrl 业务模块
- ⑤ 创建 user_router 路由模块
在app.js入口文件创建基本的服务器
//导入express模块
import express from 'express'
//创建express的服务器实例
const app=express()
//调用app.listen方法,指定端口号并启动服务器
app.listen(80,()=>{
console.log('server running at http://127.0.0.1');
})
3.创建 db 数据库 *** 作模块
创建db文件夹,在db文件夹下创建index.js
import mysql from 'mysql2'
const pool =mysql.createPool({
host:'127.0.0.1',
port:3306,
database:'my_db_01',// *** 作的数据库名称
user:'root',//登录数据库的用户名
password:'admin123'//登录数据库的密码
})
//默认导出一个支持Promise API 的pool
export default pool.promise()
4.创建user_ctrl模块
新建一个文件夹controller进行封装获取用户列表的数据,在该文件夹下创建user_ctrl.js
模块,
其中用try.....catch进行捕获异常
import db from '../db/index.js'
/* 使用ES6的按需导出语法,将getAllUser方法导出出去 */
export async function getAllUser(req,res){
try{
const [rows]=await db.query('select id,username,nickname from ev_users')
res.send({
status:0,
message:'获取用户列表数据成功!',
data:rows
})
}catch(err){
res.send({
status:1,
message:'获取用户列表数据失败!',
desc:err.message
})
}
}
5.创建API路由模块和在app.js进行导入注册
创建文件夹router,该目录下创建user_router.js模块
import express from 'express'
import { getAllUser } from '../controller/user_ctrl.js'
const router=new express.Router()
router.get('/user',getAllUser)
export default router
在app.js进行导入注册
import express from 'express'
//导入路由模块
import userRouter from './router/user_router.js'
const app=express()
//路由模块进行注册使用
app.use('/api',userRouter)
app.listen(80,()=>{
console.log('server running at http://127.0.0.1');
})
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)