目录
1.3.5 util.promisify方法进行promise风格转化
1. Promise介绍与基本使用
1.1 介绍
Promise是JS中进行异步编程的新解决方案(旧方案是单纯使用回调函数)。Promise是一个构造函数,其对象用来封装一个异步操作并可以获取其成功/失败的结果值。
异步编程,比如:fs文件操作、数据库操作、AJAX、定时器。
1.2 优点
1. 支持链式调用,可以解决回调地狱问题。(回调地狱:回调函数嵌套调用,外部回调函数异步执行的结果是嵌套的回调执行的条件。不便于阅读,不便于异常处理。)
2. 指定回调函数的方式更加灵活。
1.3 基本使用
// 构造一个Promise对象,封装一个异步操作
// resolve解决 reject拒绝 都是函数类型的数据
const p = new Promise((resolve, reject) => {
// 异步操作
setTimeout(() => {
let n = 1;
if(成功){
resolve(); // 将Promise对象的状态设置为成功
// resolve(n);
}else{
reject(); // 将Promise对象的状态设置为失败
// reject(n);
}
}, 1000);
});
// 调用then方法
p.then((value) => {
// 对象状态为成功的回调
}, (reason) => {
// 对象状态为失败的回调
});
1.3.1 fs读取文件实践
const fs = require('fs');
// 回调函数形式
// fs.readFile('../xxx/xxx.txt', (err, data) => {
// // 如果出错则抛出错误
// if(err) throw err;
// // 输出文件内容
// console.log(data.toString());
// });
// Promise形式
let p = new Promise((resolve, reject) => {
fs.readFile('../xxx/xxx.txt', (err, data) => {
// 如果出错
if(err) reject(err);
// 如果成功
resolve(data);
});
});
// 调用then方法
p.then(value => {
// 对象状态为成功的回调
console.log(value.toString());
}, reason => {
// 对象状态为失败的回调
console.log(reason);
});
1.3.2 AJAX请求实践
let p = new Promise((resolve, reject) => {
// 创建对象
const xhr = new XMLHttpRequest();
// 初始化设置请求方法和url
xhr.open('GET','https://xxx/xxx');
// 发送
xhr.send();
// 处理服务端返回的结果
xhr.onreadystatechange = function(){
//判断服务端返回了所有结果
if(xhr.readyState === 4){
//判断响应状态码成功
if(xhr.status >= 200 && xhr.status < 300){
// 输出响应体
resolve(xhr.response);
}else{
// 输出状态码
reject(xhr.status);
}
}
}
});
p.then(value=>{
// 对象状态为成功的回调
console.log(value);
}, reason=>{
// 对象状态为失败的回调
console.log(reason);
});
1.3.3 封装fs读取文件实践
// 封装一个函数mineReadFile读取文件内容
// 参数:path 文件路径
// 返回:promise 对象
function mineReadFile(path){
return new Promise((resolve, reject) => {
// 读取文件
require('fs').readFile(path, (err, data) => {
// 如果出错
if(err) reject(err);
// 如果成功
resolve(data);
});
});
}
mineReadFile('../xxx/xxx.txt')
.then(value=>{
// 输出文件内容
console.log(value.toString());
}, reason=>{
console.log(reason);
});
1.3.4 封装AJAX请求实践
// 封装一个函数sendAJAX发送GET请求
// 参数:URL
// 返回:Promise 对象
function sendAJAX(url){
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('GET',url);
xhr.send();
xhr.onreadystatechange = function(){
if(xhr.readyState === 4){
//判断响应状态码成功
if(xhr.status >= 200 && xhr.status < 300){
// 输出响应体
resolve(xhr.response);
}else{
// 输出状态码
reject(xhr.status);
}
}
}
});
}
sendAJAX('https://xxx/xxx')
.then(value=>{
console.log(value);
}, reason=>{
console.log(reason);
});
1.3.5 util.promisify方法进行promise风格转化
util.promisify(original);传入一个遵循常见的错误优先的回调风格的函数(即以(err, value) => {}回调作为最后一个参数),并返回一个返回promise的版本。
// 引入util模块
const util = require('util');
// 引入fs模块
const fs = require('fs');
// 返回一个新的函数
let mineReadFile = util.promisify(fs.readFile);
mineReadFile('./xxx/xxx.txt').then(value=>{
console.log(value);
});
1.3.6 Promise对象的状态
实例对象中的一个属性PromiseState,共有3种值:pending(初始化未决定的)、resolved/fulfilled(成功)、rejected(失败)。共有2种状态改变:pending变为resolved、pending变为rejected。
一个promise对象状态只能改变一次,无论哪种改变都会有一个结果数据,一般称为value(成功结果)/reason(失败结果)。
1.3.7 Promise对象的值
实例对象中的一个属性PromiseResult,保存异步任务成功或失败的结果。resolve()和reject()可以修改这个结果。
1.3.8 Promise工作流程
2. Promise API
1. Promise构造函数:Promise(excutor){}
1)excutor函数:执行器(resolve,reject) => {}
2)resolve函数:内部定义成功时我们调用的函数 value=>{}
3)reject函数:内部定义失败时我们调用的函数 reason=>{}
说明:excutor会在Promise内部立即同步调用,异步操作在执行器中执行。
2. Promise.prototype.then方法:(onResolved, onRejected) => {}
1)onResolved函数:成功的回调函数 (value) => {}
2)onRejected函数:失败的回调函数 (reason) => {}
说明:返回一个新的Promise对象。
3. Promise.prototype.catch方法:(onRejected) => {}
1)onRejected函数:失败的回调函数 (reason) => {}
说明:返回一个新的Promise对象。
4. Promise.resolve方法:(value) => {}
1)value:成功的数据或promise对象
说明:返回一个Promise对象。如果传入的参数为非promise对象,则返回的结果为成功
的promise对象。如果传入的参数为promise对象,则参数的结果决定了resolve的
结果。
5. Promise.reject方法:(reason) => {}
1)reason:失败的原因
说明:返回一个失败的promise对象,该对象的值为传入的参数。
6. Promise.all方法:(promises) => {}
1)promises:包含n个promise的数组
说明:返回一个新的promise。只有所有promise都成功才成功,值为所有promise成功结
果组成的数组。失败结果为失败promise对象的失败结果值,多个失败取第一个。
7. Promise.race方法:(promises) => {}
1)promises:包含n个promise的数组
说明:返回一个新的promise,第一个完成的promise的结果状态就是它的结果状态。
3. Promise关键问题
3.1 修改对象的状态
1. resolve函数 pending => fulfilled(resolved)
2. reject函数 pending => rejected
3. 抛出问题 throw '出错了!' pending => rejected
3.2 执行多个回调
一个promise指定多个成功(失败)回调函数时,当promise改变为对应状态时都会调用。
3.3 改变状态与指定回调的顺序
1. 修改状态和.then方法谁先谁后?
都有可能,正常情况下是先指定回调再改变状态,但也可以先改状态再指定回调。当执行
器函数中是一个异步任务时,先指定回调再改状态,是同步任务时先改状态再指定回调。
2. 如何先改状态再指定回调?
1)在执行器中直接调用resolve()/reject()
2)延迟更长时间才调用then()
3. 什么时候才能得到数据(回调函数什么时候执行)?
1)如果先改状态,then方法调用时就执行。
2)如果先指定回调,改变状态后执行。
3.4 then方法返回结果
返回一个新的promise,新promise的结果状态由then()指定的回调函数执行的结果决定。
1)如果抛出异常,新promise变为rejected,reason为抛出的异常。
2)如果返回的是非promise的任意值,新promise变为resolved,value为返回的值。
3)如果返回的是另一个promise,此promise的结果就会成为新promise的结果。
3.5 串联多个任务
promise的then()返回一个新的promise,因此可以开成then()的链式调用,串联多个同步/异步任务。
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('OK');
}, 1000);
});
p.then(value => {
return new Promise((resolve, reject) => {
resolve("SUCCESS");
});
}).then(value => { // 这个then()返回的promise的状态由它指定的回调函数的返回值决定,它的回调函数没有写返回值,就是undefined,因此then()返回的promise就是一个成功的promise且返回的结果就是undefined
console.log(value); // 打印SUCCESS
}).then(value => {
console.log(value); // 打印undefined
})
3.6 异常穿透
当使用promise的then链式调用时,可以在最后指定失败的回调,前面任何操作出了异常,都会传到最后失败的回调中处理。
3.7 中断promise链
只有返回一个pending状态的promise对象(return new Promise(() => {});)才能中断promise链条,在中间中断,不再调用后面的回调函数。
4. Promise自定义封装
class Promise{
// 构造方法
constructor(executor){
// 添加属性
this.PromiseState = 'pending';
this.PromiseResult = null;
// 声明属性
this.callbacks = [];
// 保存实例对象的 this 的值
const self = this;
// resolve 函数
function resolve(data){
//判断状态
if(self.PromiseState !== 'pending') return;
// 修改对象的状态(PromiseState)
self.PromiseState = 'fulfilled';
// 设置对象结果值(PromiseResult)
self.PromiseResult = data;
// 调用成功的回调函数
setTimeout(()=>{
self.callbacks.forEach(item => {
item.onResolved(data);
})
})
}
// reject 函数
function reject(data){
//判断状态
if(self.PromiseState !== 'pending') return;
// 修改对象的状态(PromiseState)
self.PromiseState = 'rejected';
// 设置对象结果值(PromiseResult)
self.PromiseResult = data;
// 调用失败的回调函数
setTimeout(()=>{
self.callbacks.forEach(item => {
item.onRejected(data);
})
})
}
try{
// 同步调用执行器函数
executor(resolve, reject);
}catch(e){
// 修改 promise 对象状态为失败
reject(e);
}
}
// then 方法封装
then(onResolved, onRejected){
const self = this;
// 判断回调函数参数
if(typeof onResolved !== 'function'){
onResolved = value => value;
}
if(typeof onRejected !== 'function'){
onRejected = reason =>{
throw reason;
}
}
return new Promise((resolve, reject) => {
// 封装函数
function callback(type){
try{
// 获取回调函数的执行结果
let result = type(self.PromiseResult);
// 判断
if(result instanceof Promise){
// 如果是 Promise 类型的对象
result.then(v=>{
resolve(v);
}, r=>{
reject(r);
})
}else{
//结果的对象状态为成功
resolve(result);
}
}catch(e){
reject(e);
}
}
// 调用回调函数
if(this.PromiseState === 'fulfilled'){
// then 方法回调异步执行
setTimeout(()=>{
callback(onResolved);
});
}
if(this.PromiseState === 'rejected'){
setTimeout(()=>{
callback(onRejected);
});
}
// 判断pending状态(异步任务)
if(this.PromiseState === 'pending'){
// 保存回调函数
this.callbacks.push({
onResolved: function(){
callback(onResolved);
}
onRejected: function(){
callback(onRejected);
}
});
}
})
}
// catch 方法
catch(onRejected){
return this.then(undefined, onRejected);
}
// 添加 resolve 方法(属于promise函数对象,不是属于实例对象)
static resolve(value){
// 返回 promise 对象
return new Promise((resolve, reject) => {
if(value instanceof Promise){
value.then(v=>{
resolve(v);
}, r=>{
reject(r);
})
}else{
//结果的对象状态为成功
resolve(value);
}
});
}
// 添加 reject 方法
static reject(reason){
// 返回 promise 对象
return new Promise((resolve, reject) => {
reject(reason);
});
}
// 添加 all 方法
static all(promises){
// 返回结果为 promise 对象
return new Promise((resolve, reject) => {
// 声明变量
let count = 0;
let arr = [];
// 遍历
for(let i=0; i<promises.length; i++){
promises[i].then(v => {
// 得知对象的状态是成功
count++;
// 将当前promise对象成功的结果存入到数组中
arr[i] = v;
// 每个promise对象都成功
if(count === promise.length){
resolve(arr);
}
}, r => {
reject(r);
});
}
});
}
// 添加 race 方法
static race(promises){
return new Promise((resolve, reject) => {
for(let i=0; i<promises.length; i++){
promises[i].then(v => {
resolve(v);
}, r => {
reject(r);
});
}
});
}
}
5. async与await
5.1 async函数
1. 函数的返回值为Promise对象。
2. promise对象的结果由async函数执行的返回值决定。(和then方法返回结果规则一样)
1)如果抛出异常,结果为rejected,reason为抛出的异常。
2)如果返回的是非promise的任意值,结果为resolved,value为返回的值。
3)如果返回的是另一个promise,返回promise的结果就会成为它的结果。
5.2 await表达式
await右侧的表达式一般为promise对象,但也可以是其他值。
1. 如果表达式是promise对象,await返回的是promise成功的值。
2. 如果表达式是其他值,直接将此值作为await的返回值。
注意:1. await必须写在async函数中,但async函数中可以没有await。
2. 如果await的promise失败,就会抛出异常,需要通过try...catch捕获处理。
5.3 async与await结合实践
5.3.1 读取文件内容
// 读取 resource 文件夹下 3 个文件内容
const util = require('util');
const fs = require('fs');
const mineReadFile = util.promisify(fs.readFile);
async function main(){
try{
// 读取第一个文件的内容
let data1 = await mineReadFile('./resource/1.txt');
// 读取第二个文件的内容
let data2 = await mineReadFile('./resource/2.txt');
// 读取第三个文件的内容
let data3 = await mineReadFile('./resource/3.txt');
}catch(e){
console.log(e);
}
}
5.3.2 发送AJAX请求
function sendAJAX(url){
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open('GET',url);
xhr.send();
xhr.onreadystatechange = function(){
if(xhr.readyState === 4){
//判断响应状态码成功
if(xhr.status >= 200 && xhr.status < 300){
// 输出响应体
resolve(xhr.response);
}else{
// 输出状态码
reject(xhr.status);
}
}
}
});
}
async function main(){
let data = await sendAJAX('https://xxx/xxx');
console.log(data);
}