前后端交互(一)——Node + Gulp + HTTP

目录

一.Node.js 基础

二.模块加载及第三方包

1.Node.js 模块化开发  

2.系统模块  

3.第三方模块  

4.package.json文件  

5.Node.js中模块的加载机制

三.请求响应原理及HTTP协议

四.HTTP请求与响应处理  

1.请求参数

2.路由 

3.静 / 动态资源

五.Node.js异步编程

1.同步API, 异步API

2.同 / 异步API 代码执行顺序

3.Node.js 中的回调地狱

4.Promise

5.异步函数


一.Node.js 基础

1.Node 开发概述

  • 学习服务器端开发目的:网站业务逻辑前置,学习前端技术需要后端技术支撑(Ajax),和后端人员更好配合
  • 服务器端开发要做的事情:实现网站的业务逻辑、数据的增删改查
  • Node:一个 基于Chrome V8引擎的 JavaScript代码 运行环境
  • 关于运行环境:
  1. 浏览器(软件)能够运行 JavaScript代码,浏览器 就是 JavaScript代码 的运行环境
  2. Node(软件)能够运行 JavaScript代码,Node 就是 JavaScript代码 的运行环境
  • Node优势:使用 JavaScript语法,生态系统活跃(用的人多),开源库,前端开发工具大多基于Node开发
  • Node.js 的组成:
  • ESx 是 JS核心,DOM/BOM 知识浏览器提供给 JS 的接口,和 NodeAPI 同等
  • Node 环境 提供的一些附加 API:包括 文件、网络、路径 等等
  • 命令行窗口运行 JS语法: node 文件名.js

2.Node.js 全局对象 global

  • 在浏览器中 全局对象是 window,在Node中 全局对象是 global
  • 全局对象所包含方法,global 可以省略:
  1. console.log()     在控制台中输出
  2. setTimeout()     设置超时定时器
  3. clearTimeout()  清除超时时定时器
  4. setInterval()      设置间歇定时器
  5. clearInterval()   清除间歇定时器

3.命令行基本使用

  • shift+右键:在当前目录下打开命令行工具
  • node -v:查看版本号(是否安装成功)
  • 上一级目录:../   当前目录:./
  • Tab键:自动补全文件名
  • ↑:重复输入上一条命令

二.模块加载及第三方包

1.Node.js 模块化开发  

  • JavaScript 存在两大问题:文件依赖 和 命名冲突
  • 软件中的模块化开发:一个功能一个模块,抽离一个模块不影响其他功能运行

1.1 Node.js 模块化开发规范

  • 一个 JS文件 就是一个模块,模块内部定义的变量函数,默认情况下在外部无法得到
  • 模块内部使用 exports 进行成员 导出, 使用 require 导入 其他模块
  • 模块成员导出方法:
  • 模块成员导出的另一种方法:module.exports.xx
  • exports 是 module.exports 的别名(地址引用关系,指向同一块内存),导出对象最终以 module.exports为准
 // 模块成员导出: a.js
 let version = 1.0;  // 在模块内部定义变量
 const sayHi = name => `您好, ${name}`;  // 在模块内部定义方法
 const add = (n1, n2) => n1 + n2;

 exports.version = version;    // 向模块外导出数据 
 exports.sayHi = sayHi;        // 左边导出名(可以自定义) 右边本文变量名(不可以随便改) 
 module.exports.add = add;

// 模块成员导入:b.js
let af = require('./a.js');    // 在 b.js模块中导入模块a,
// let af = require('./a');    // .js可以省略
console.log(af.version);       // 输出 a模块中的 version变量
console.log(af.add(10, 20));   // 调用 a模块中的 sayHi方法

2.系统模块  

  • 系统模块定义:Node运行环境 提供的API 就是系统模块

2.1 文件系统模块

  • 文件(file)系统(system)模块
  • 读取文件内容:fs.reaFile('文件路径' [,'文件编码'],  callback);
  • 写入文件内容:fs.writeFile('文件路径', '数据', callback);
  • fs.readFile('./01.helloworld.js', 'utf8', (err, doc) => {...
  • 读取文件是对硬盘操作,有时间损耗,不能立刻获得读取结果,所以用回调函数获取结果
  • 如果文件读取出错,err是一个对象,包含错误信息,如果文件读取正确,err输出null
  • doc 是文件读取的结果
  • fs.writeFile('./demo.txt', '即将要写入的内容', err => {
  • 如果没有 demo.txt文件,系统会自动创建,最终文件写入内容,会覆盖文件原本的内容

2.2 系统模块path 路径操作

  • 路径拼接原因:不同操作系统的路径分隔符不统一,Windows 上是 \   /,Linux 上是 /
  • Linux 通常作为网站服务器
  • 路径拼接语法:path.join('路径', '路径', ...)
  • const path = require('path'); // 会自动判断系统类型,决定路径分隔符的样式
  • const finalPath = path.join('public', 'uploads','avatar'); // public/uploads/avata

2.3 相对路径 vs 绝对路径

  • 大多数情况使用 绝对路径,因为相对路径相对的是 命令行工具的当前工作目录
  • __dirname:获取当前文件所在的 绝对路径
  • fs.readFile(path.join(__dirname, '01.helloworld.js'), 'utf8', (err, doc) => 
  • D:\Desktop\Desktop\node\Node1\01.helloworld.js(绝对路径+文件名拼接成功)

3.第三方模块  

  • 别人写好的、具有特定功能的、能直接使用的模块即 第三方模块
  • 由于第三方模块通常都是由多个文件组成并且被放置在一个文件夹中,所以又名 
  • 第三方模块的两种存在形式:
  1. js文件 形式存在,提供实现项目功能的 API
  2. 命令行工具 形式存在,辅助项目开发

3.1 获取第三方模块 npmjs.com

  • npm (node package manager) : node 第三方模块管理工具(npm本身就是第三方模块)
  1. 下载:npm install 模块名称
  2. 卸载:npm unintall 模块名称
  3. 可以同时下载多个第三方模块 名字用空格隔开
  • 全局安装与本地安装:
  1. 命令行工具:全局安装(让所有项目都可以使用该工具)
  2. 库文件:本地安装(只有当前项目可以使用)

3.2 第三方模块 nodemon

  • nodemon 是一个 命令行工具,自动重新执行 修改并保存的文件
  1. 使用 npm install nodemon –g 下载(-g:全局安装)
  2. 在命令行工具中用 nodemon命令 替代 node命令 执行文件
  • 修改的文件被保存后,nodemon命令 会自动重新执行文件,执行结束后输出下面的内容:
  • [nodemon] clean exit - waiting for changes before restart (挂起,等待重新执行该文件)
  • 如果需要退出 nodemon命令,按 ctrl+c 表示打断当前命令,就会自动跳转回 node命令了

3.3 第三方模块 nrm

  • nrm ( npm registry manager ):npm下载地址切换工具,也是一个 命令行工具
  1. 使用 npm install nrm –g 下载它
  2. 查询可用下载地址列表 nrm ls
  3. 切换 npm下载地址 nrm use 下载地址名称
  • 将国外下载地址 转换为 国内下载地址,提高下载速度

3.4 第三方模块 Gulp

  • Gulp:前端构建工具,将机械化操作编写成任务
  • Gulp功能:
  1. 项目上线,HTML、CSS、JS文件压缩合并
  2. 语法转换(es6、less ...) // ES6并未被浏览器完全支持,需要工具转换成ES5语法
  3. 公共文件抽离
  4. 修改文件浏览器自动刷新
  • Gulp使用:
  1. 使用 npm install gulp 下载 gulp库文件,作为第三方模块存在 node_modules文件夹
  2. 在项目根目录下建立 gulpfile.js文件
  3. 重构项目的文件夹结构:src目录:放置源代码文件 ,dist目录:放置构建后文件
  4. 在 gulpfile.js文件 编写任务
  5. 在 命令行工具 执行 gulp任务
  6. 执行 gulp项目某个特定任务需要 gulp命令,使用 node命令 会导致所有任务都被执行
  7. gulp命令需要(npm install gulp-cli -g)安装使用,使用方法:gulp+任务名
  • Gulp提供的方法:
  1. gulp.src():获取任务要处理的文件
  2. gulp.dest():输出处理文件(一定要写在.pipe()中,.pipe()相当于复制文件)
  3. gulp.task('任务名称', ()=>{...}):建立 gulp任务
  4. gulp.watch():监控文件的变化
  • gulp.task('first', () => {gulp.src('./src/css/base.css').pipe(gulp.dest('dist/css'));
  • dist目录下没有css文件夹,gulp会自动创建
  • Gulp插件(命令行下载命令):
  1. gulp-file-include:抽取公共文件(npm install gulp-file-include
  2. gulp-htmlmin:html文件压缩(npm install gulp-htmlmin)
  3. gulp-less:less语法转化(npm install gulp-less)
  4. gulp-csso:css文件压缩(npm install gulp-csso
  5. gulp-uglify:js文件压缩(npm install gulp-uglify
  6. browsersync 浏览器实时同步
  7. gulp-bable:ES6 转换为 ES5(npm install gulp-babel @babel/core @babel/preset-env)
  • Gulp插件使用方法:
  • 查询文档、命令行下载插件、复制文档代码在 gulpfile.js中 引入插件、构建任务、调用插件、gulp命令执行任务
const gulp = require('gulp'); // 引用 gulp模块
const fileinclude = require('gulp-file-include'); // 抽取公共代码
const htmlmin = require('gulp-htmlmin'); // 压缩 html文件
const less = require('gulp-less'); // 将less语法转换为css语法
const csso = require('gulp-csso'); // 压缩 css文件
const babel = require('gulp-babel'); // ES6 转换为 ES5
const uglify = require('gulp-uglify'); // 压缩 js文件
// 使用 gulp.task 建立任务
// 1.任务的名称  2.任务的回调函数
gulp.task('first', () => {
    console.log('茶茶子人生中的第一个gulp任务执行了');
    gulp.src('./src/css/base.css') // 1.使用 gulp.src 获取要处理的文件
        .pipe(gulp.dest('dist/css')); // dist目录下没有css文件夹,gulp会自动创建
});
// html任务
gulp.task('htmlmin', () => {
    gulp.src('./src/*.html') // *.html 全部html文件
        // 先抽取公共代码 在 src目录中新建 common文件夹,在 common文件夹中 新建 header.html 
        // 将所有 html文件中相同的头部剪切到 header.html中
        // 在被剪掉头部的 html文件中添加:@@include('./common/header.html')
        .pipe(fileinclude())
        .pipe(htmlmin({ collapseWhitespace: true })) // 再压缩 html文件代码
        .pipe(gulp.dest('dist'));
});
// css任务
gulp.task('cssmin', () => {
    gulp.src(['./src/css/*.less', './src/css/*.css'])
       
        .pipe(less()) // 将less语法转换为css语法
        .pipe(csso()) // 压缩 css文件
        .pipe(gulp.dest('dist/css'))
});
// js任务
gulp.task('jsmin', () => {
    gulp.src('./src/js/*.js')
        // 先转换为 ES6语法
        .pipe(babel({
            // 判断当前运行环境 将代码转换为当前运行环境所支持的代码
            presets: ['@babel/env']
        }))
        .pipe(uglify())  // 再压缩 js文件
        .pipe(gulp.dest('dist/js'))
});
// 复制文件夹
gulp.task('copy', () => {
    gulp.src('./src/images/*')
        .pipe(gulp.dest('dist/images'));
    gulp.src('./src/lib/*')
        .pipe(gulp.dest('dist/lib'))
});
// 不同版本的gulp,对下面语法支持度是不同的 为了同时执行多个 gulp任务
// 最好的办法是 挨个任务执行,在命令行中输入:gulp htmlmin cssmin jsmin copy
// 构建任务 default+任务名数组,直接输入gulp 就可以执行所有任务
// gulp.task('default', ['htmlmin', 'cssmin', 'jsmin', 'copy']); // gulp3
// gulp.task('default', gulp.series('htmlmin', 'cssmin', 'jsmin', 'copy')) // gulp4

4.package.json文件  

  • node_modules文件夹 问题:文件太多太大,模块依赖关系及版本需要被记录
  • package.json文件 作用:项目描述文件,记录项目名称、作者、github地址、当前项目依赖的第三方模块等 
  • 原版项目中,使用 npm init -y 命令生成 package.json文件
  • 开发人员拿的项目不包括 node_modules文件夹,打开项目根目录,在命令行中输入 npm install 会直接根据 package.json文件 下载对应的 项目依赖 和 开发依赖,生成 node_modules文件夹
  • 关于项目别名:当文件名字很长时,在 package.json 中的 “script” 中记录别名
  • 运行文件时在命令行中输入:npm run 别名 / node 原名.js

4.1 项目依赖

  • 项目依赖:在项目的开发阶段 和 线上运营阶段 都需要依赖的第三方包
  • npm install 包名 --production命令 将包添加到 package.json文件 的 dependencies 字段中

4.2 开发依赖

  • 开发依赖:在项目的开发阶段 需要依赖,线上运营阶段 不需要依赖的第三方包
  • npm install 包名 --save-dev命令 将包添加到 package.json文件 的 devDependencies 字段中
  • 总结:
  1. 同时下载项目依赖、开发依赖:npm install
  2. 只下载项目依赖:npm install 包名 --production
  3. 只下载开发依赖:npm install 包名 --save-dev

4.3 package-lock.json文件作用

  • 记录了模块与模块之间的依赖关系、模块版本、模块下载地址
  • 锁定包的版本,确保不会因为包 版本不同 而产生问题  
  • 加快下载速度,记录了项目所依赖的第三方包 和 下载地址

5.Node.js中模块的加载机制

5.1 当模块拥有路径但没有后缀时

  • require('./find.js');
  • require('./find'); 
  • require方法根据 模块路径 查找模块,如果是完整路径,直接引入模块
  • 如果模块后缀省略,先找 同名JS文件 再找 同名JS文件夹
  • 如果找到了 同名文件夹,找文件夹中的 index.js
  • 如果没有 index.js,查看该文件夹中的 package.json文件 查找 main.js入口文件
  • 如果入口文件不存在 或 没有指定入口文件,就会报错,模块没有被找到 

5.2 当模块没有路径且没有后缀时

  • require('find');
  • Node.js会假设它是第三方模块
  • Node.js会去node_modules文件夹,看是否有 同名JS文件,再看是否有 同名文件夹
  • 如果是文件夹,看里面是否有 index.js
  • 如果没有 index.js,查看该文件夹中的 package.json 文件, 查找 main.js入口文件
  • 如果入口文件不存在 或 没有指定入口文件,就会报错,模块没有被找到 

三.请求响应原理及HTTP协议

1.服务器端基础概念  

  • 网站应用程序分为两大部分:客户端、服务器端
  • 客户端:在浏览器中运行的部分,用户看到并与之交互的界面
  • 服务器端:在服务器中运行的部分,负责 存储数据 和 处理应用逻辑
  • 网站服务器:能够提供网站访问服务的机器,它能 接收客户端请求,并对请求做出响应
  • IP地址:互联网中设备唯一标识,Internet Protocol Address-互联网协议地址
  • 域名:平时上网所用的 网址,输入的网址最终会转换为 IP地址,进而访问指定网站服务器
  • 端口:区分服务器提供的不同的服务    
  • URL:统一资源定位符,平时所说的 网页地址 即是URL
  1. 传输协议://服务器IP或域名:端口/资源所在位置标识
  2. http://www.Lyrelion.com/news/20200409/index.html
  • HTTP:超文本传输协议,提供了一种 发布和接收 HTML页面 的方法
  • 开发过程中,客户端 和 服务器端 用同一台电脑   ,这台电脑称为 网站服务器
  • 先用 IP / 域名 找到服务器,再利用 端口 区分服务器提供的不同服务
  • 本机域名:localhost
  • 本地IP:127.0.0.1

2.创建 web服务器  

  • Node.js 也是 事件驱动 的语言
  • 创建 web服务器的步骤:
  1. 引入创建网站服务器的系统模块: const http = require('http');
  2. 调用http 创建服务器对象 createServer(): const app = http.createServer();
  3. 服务器.on事件侦听: app.on('request', (req, res) => { res.end('<h1>hi, user</h1>'); });
  4. 给服务器添加监听端口:app.listen(3000);

3.HTTP协议  

  • 规定了 如何从 网站服务器 传输超文本 到 本地浏览器,是客户端(用户)和服务器端(网站)请求和应答的标准
  • 报文:HTTP请求和响应过程中传递的数据块,包括要传送的数据和附加信息,并且要遵守规定格

四.HTTP请求与响应处理  

  • 客户端向服务器端发送请求时,需要携带信息,通过 请求参数 传给服务器端
  • 获取请求地址:console.log(req.url);
  • 获取请求报文信息:console.log(req.headers['accept']); // 获取具体信息,加 ['键名'] 即可
  • 服务器对客户端作出相应:res.end('welcome to listpage');
  • 获取请求方式:if (req.method == 'POST')

1.请求参数

  • 在浏览器中,输入网址:localhost:3000,命令行会输出两次 GET
  1. 第一次GET:输入网址请求,手动触发
  2. 第二次GET:浏览器图标请求,是自动发出的
  • 在浏览器中,提交表单,命令行输出POST,GET
  1. POST:提交表单的行为
  2. GET:提交表单后,浏览器有个默认的跳转行为

1.1 GET请求参数

  1. 参数 被放置在 浏览器地址栏,例如:http://localhost:3000/?name=TeaMeow&age=21
  2. 参数获取需要借助 系统模块url:url.parse( 要解析的url地址,将查询参数解析成对象形式 ):解析 url地址
  3. 解构请求地址中的参数:let { query, pathname } = url.parse(req.url, true);
  4. req.url对象包含很多属性,如:pathname、query
  5. pathname:客户端请求地址,不包含请求参数的请求地址,if (pathname == '/index' || pathname == '/') {.......
  6. query:请求参数对象,包含各种请求参数及其值,query.name,query.age
  • 运行服务器app.js,打开浏览器输入:localhost:3000/?name=TeaMeow&age=21,命令行得到  TeaMeow,21

1.2 POST请求参数

  • 参数 被放置在 请求体(报文)中 进行传输
  • 参数获取需要借助 data事件 和 end事件
  • 参数获取需要借助 querystring系统模块 将 字符串格式参数 转换为 对象格式
  1. data 当请求参数传递的时候,触发 data事件
  2. end 当参数传递完成的时候,触发 end事件
  • post请求参数不是一次性传完的,而是一部分一部分传,服务器一部分一部分接收
  • 所以先定义一个变量,拼接每一次接受的参数,参数传递晚了再返回拼接结果
  • let postParams = '';  req.on('data', params => { postParams += params; });
  • 参数传递完成 转换接受到的 post字符串 将它变成对象格式
  • req.on('end', () => {console.log(querystring.parse(postParams));});
  • post参数不能用 url.pause() 将参数转换为对象形式,因为post参数不在地址栏,而在请求体(报文)中
  • 在浏览器检查工具 Network / Form Data 选项里,存储了 post请求参数
  •  POST参数存储位置(请求体(报文)):
  • 命令行运行服务器文件app.js,再运行form.html,提交表单后,浏览器得到服务器反馈ok res.end('ok');
  • 控制台接收到post参数:{ username: 'TeaMeow', password: '233333' }

1.3 响应头

  • res.writeHead(200, {'content-type': 'text/html;charset=utf8' // 可以解析 html标签了});
  • 设置响应头信息,参数一:http状态码,参数二:响应内容类型
  • HTTP状态码:
  • 200 请求成功
  • 404 请求的资源没有被找到
  • 500 服务器端错误
  • 400 客户端请求有语法错误
  • 响应头内容类型 content-type
  • text/html……text/html;charset=utf8
  • text/css
  • application/javascript
  • image/jpeg
  • application/json

2.路由 

  • 路由:指 客户端请求地址 与 服务器端程序代码 的对应关系,即请求什么响应什么,跳转到正确的网页
  • const pathname = url.parse(req.url).pathname;    解释如下:
  • req.url 获取了请求路径请求参数,url.parse将其由字符串处理为对象格式,其中的属性 pathname就是不包含请求参数的单纯的请求路径,我们用一个变量接收这个请求路径,用于路由判断
  • 实现路由功能:①获取客户端的请求方式 ②获取客户端的请求地址 ③按顺序判断请求方式和请求地址
const http = require('http'); // 引入系统模块http
const app = http.createServer(); 、// 创建网站服务器
const url = require('url');
app.on('request', (req, res) => { // 为网站服务器对象添加请求事件
	const method = req.method.toLowerCase(); // 获取请求方式 默认大写,所以要忽略大小写
	const pathname = url.parse(req.url).pathname;	// 获取请求地址
	// 响应报文的处理:作出这个处理是为了避免用户看到的响应是一堆乱码
	res.writeHead(200, {
		'content-type': 'text/html;charset=utf8'
	});
    // 实现路由功能:①获取客户端的请求方式 ②获取客户端的请求地址
	if (method == 'get') {
		if (pathname == '/' || pathname == '/index') {
			res.end('欢迎来到首页')
		}else if (pathname == '/list') {
			res.end('欢迎来到列表页')
		}else {
			res.end('您访问的页面不存在')
		}
	}else if (method == 'post') {
        // 代码同 GET
	}});
app.listen(3000);    // 一定不要忘记监听端口
console.log('服务器启动成功')

3.静 / 动态资源

  • 静态资源:服务器端不需要处理,可以直接响应给客户端的资源,如 CSS、JavaScript、image文件
  • 动态资源:相同的请求地址 + 不同的参数 = 不同的响应资源
  • http://www.lyrelion.cn/article?id=1 http://www.lyrelion.cn/article?id=2
  • const mime = require('mime');:根据当前请求路径,分析请求资源类型(npm install mime)单独下载
  • 获取用户不带参数的请求路径:let pathname = url.parse(req.url).pathname;
  • 处理不带参数的请求路径:pathname = pathname == '/' ? '/default.html' : pathname;
  • 获取静态资源在服务器中的 真实路径let realPath = path.join(__dirname, 'public' + pathname);​​​​​​​
  • 根据请求路径 确定静态资源响应类型:let type = mime.getType(realPath)
  • 读取静态资源文件:fs.readFile(realPath, (error, result) => {...
  • 对响应报文做相应的格式化处理: res.writeHead(404, {  'content-type': ...
  • 服务器响应:res.end('文件读取失败'); / res.end(result);

五.Node.js异步编程

1.同步API, 异步API

  • 同步API:只有 当前API 执行完成后,才能继续执行下一个API
  • 异步API:异步API 的执行 不会阻塞 后续代码执行,比如定时器作为 异步API 后输出,后续代码先输出
  • 同步API 可以从 返回值(return) 中拿到执行结果,异步API 不可以
  • 异步API 可以通过 回调函数传参 的方式拿到执行结果

2.同 / 异步API 代码执行顺序

  •  node会先将所有 同步API 按顺序执行 console.log('代码开始执行') console.log('代码结束执行') 
  • 中途遇到的 异步API 会被放入 异步代码执行区 setTimeout(function () {2s}、setTimeout(function () {0s}
  • 紧接着把 异步API 对应的 回调函数 放到 回调函数队列 console.log('2s')、console.log('0s')
  •  此时 异步代码执行区 和 回调函数队列 中的代码均未被执行
  • 同步API 执行完毕后,node.js 会依次执行异步代码执行区代码,按照定时器决定执行哪个回调函数,将那个回调函数放入同步代码执行区,即 定时器先结束的回调函数 先执行 console.log('0s') console.log('2s')
  • 最终结果:代码开始执行、代码结束执行、0s、2s

3.Node.js 中的回调地狱

  • 依次读取:A文件、B文件、C文件

4.Promise

  • Promise 出现的目的:解决 Node.js异步编程 回调地狱,分离 异步API执行 和 结果处理
  • 读取结果 将不在 回调函数内 进行处理
  • let promise = new Promise((resolve, reject) => {
  • fs.readFile('./100.txt', 'utf8', (err, result) => {​​​​​​​​​​​​​​
  • if (err != null) { reject(err); // 出现错误,调用 reject()回调函数,将 err传递到外面}
  • else { resolve(result); // 读取成功,调用 resolve()回调函数,将 result传递到外面}
  • 处理读取结果:链式编程 promise.then().catch()
  • promise.then((result) => { console.log(result); }).catch((err) => {console.log(err); })
  • 使用promise 改造回调地狱:
const fs = require('fs');
// 为了依次执行,需要依次调用函数 获得返回值:return new Promise()...
function p1 () {
	return new Promise ((resolve, reject) => { 
		fs.readFile('./1.txt', 'utf8', (err, result) => {
			resolve(result)    // 如果当前文件读取成功 会通过 resolve()方法将读取结果传递到外面
		})});}
function p2 () {
	return new Promise ((resolve, reject) => {
		fs.readFile('./2.txt', 'utf8', (err, result) => {
			resolve(result)
		})});}
function p3 () {
	return new Promise ((resolve, reject) => {
		fs.readFile('./3.txt', 'utf8', (err, result) => {
			resolve(result)
		})});}
p1().then((r1)=> { // p1promise对象读取结果
	console.log(r1);
	return p2(); // return得到的是 p2promise对象
})
.then((r2)=> {  // p2promise对象读取结果
	console.log(r2);
	return p3();
})
.then((r3) => {
	console.log(r3)
})

5.异步函数

  • 异步函数 是异步编程语法的 终极解决方案,将异步代码写成同步形式,不再嵌套
  • async关键字:
  • 普通函数定义前加 async关键字 变成异步函数
  • 异步函数 默认返回 promise对象:Promise{undefined}

  • 异步函数使用 return关键字 进行结果返回,结果被包裹在 promise对象中,return代替了 resolve()方法
  • 异步函数使用 throw关键字 抛出程序异常,throw代替了 reject()方法
  • 调用异步函数 再链式调用 then方法 获取执行结果
  • 调用异步函数 再链式调用 catch方法 获取错误信息
  • await关键字(只能出现在异步函数中):
  • await xxx():暂停异步函数的执行,等待promise对象返回结果后再向下执行函数
async function p1() {
    throw 'Some Error'
    return 'p1'; // 相当于 return替换以前的 resolve()方法
}
async function p2() {
    return 'p2'; // 异步函数默认的返回值是promise对象
}
// p1 () p2 () p3 () 都返回 Promise对象
async function run() { //此异步函数 负责 按顺序执行其他异步函数
    let r1 = await p1() // await使得只有当 p1()得到结果之后 才会继续向下执行
    let r2 = await p2()
    console.log(r1)
    console.log(r2)
}
run(); // p1 p2 
  •  依次读取文件 异步函数 终极解决方案:
  • readFile()方法本身是通过 返回值 获取结果的,也就是本身无法返回 Promise对象 这样不能加 async关键字
  • 改造现有异步函数api 让其返回 promise对象 从而支持异步函数语法 即可以加 async
  • const promisify = require('util').promisify;
  • 调用 promisify方法 改造现有异步API 让其返回 promise对象
  • const readFile = promisify(fs.readFile);
const fs = require('fs');
// 改造现有异步函数api 让其返回 promise对象 从而支持异步函数语法 即可以加 async
const promisify = require('util').promisify;
// 调用 promisify方法 改造现有异步API 让其返回 promise对象
const readFile = promisify(fs.readFile);
// readFile()方法本身是通过 返回值 获取结果的
// 也就是本身无法返回 Promise对象 这样不能加async关键字
async function run () {
	let r1 = await readFile('./1.txt', 'utf8')
	let r2 = await readFile('./2.txt', 'utf8')
	let r3 = await readFile('./3.txt', 'utf8')
	console.log(r1)
	console.log(r2)
	console.log(r3)
}
run();

 

 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Lyrelion

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值