Promise
Promise 是异步编程的一种解决方案,可以简化代码、支持链式调用,解决回调地狱
异步任务有哪些
- 定时器
- 数据库操作
- Ajax请求
- fs文件操作
Promise基本使用
const but=document.getElementById("but")
but.addEventListener("click",function (){
let randomNumber=Math.ceil(Math.random()*100)
// 创建 promise 实例
const p = new Promise(function (resolve, reject){
setTimeout(()=>{
if(randomNumber<30){
//成功的状态
resolve(n);
}else{
//失败的的状态
reject(n);
}
},1000)
})
// 这里用then函数执行成功或者失败的回调
// 参数需要两个回调,第一个为成功的回调,第二个为失败的回调
p.then(value => {
alert('恭喜,您中奖了' + value);
},reason => {
alert('很遗憾,您没有中奖' + reason);
})
})
Promisify()
promisify()
是util
封装好的工具,可将传入参数变为promise
风格
// 将fs模块promise化流程
// 1、引入所需要的模块
const util = require('util');
const fs = require('fs');
// 2、将fs.readFile变成promise风格
const promiseFS = util.promisify(fs.readFile);
// 3、正常使用
promiseFS('text').then(value => {
console.log(value);
}, () => {
console.log('error');
})
Promise的流程
promiseState
-
状态值
-
pending 未决定的
-
resolve / fullfilled 成功
-
rejected 失败
-
-
Promise 状态改变
- pending 变为 resolved
- pending 变为 rejected
promiseResult
- 保存异步任务成功 / 失败的结果
- 只能由resolve、reject来修改
流程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bGx6hqcq-1682324391955)(D:\Typora\photos\image-20221108164221889.png)]
API
构造函数
函数在Promise内部立即同步调用执行
const p = new Promise((resolve, reject) => {
console.log(111);
})
console.log(222);
// 111
// 222
then方法
p.then(resolve(){
}, reject(){
})
catch方法
- 指定失败的回调
p.catch(reson => {
console.log(reason);
})
resolve方法
- 将一个元素转换成一个成功 / 失败的promise对象,成功 / 失败取决于传入参数
let p1 = Promise.resolve(521);
console.log(p1) // promise对象
let p2 = Promise.resolve(
new Promise((resolve, reject) => {
// resolve('OK');
reject("Error");
})
);
// 如果不对出错进行处理(指定错误回调),控制台就会报错
p2.then(value => {
console.log(value);
},reason => {
// Error
console.log(reason);
});
console.log(p2)
reject方法
- 将传入的参数转换成失败的promise对象(promiseState为rejected)
let p1 = Promise.reject(521);
let p2 = Promise.reject('haha');
// 即便传入成功的,得到的promise对象状态也是失败的
let p3 = Promise.reject(
new Promise((resolve, reject) => {
resolve("OK");
})
);
p3.then((value) => {
console.log(value);
},(reason) => {
console.log(reason);
});
console.log(p1);
console.log(p2);
console.log(p3);
all方法
- 传入一个promise的数组,返回一个新的promise
- 其promiseState为传入数组中的promise对象状态逻辑与结果(全为成功才是成功,一个失败全失败)
- 其promiseResult结果为成功,则promiseResult的结果为传入数组各promiseResult的结果组成的数组,若失败,promiseResult的结果为第一个失败的对象中promiseResult值
let p1 = new Promise((resolve, reject) => {
resolve("OK");
});
let p2 = Promise.resolve('Success');
let p3 = Promise.resolve("Oh Yeah");
var result = Promise.all([p1, p2, p3]);
console.log(result);
/*
Promise
[[Prototype]]: Promise
[[PromiseState]]: "fulfilled"
[[PromiseResult]]: Array(3)
0: "OK"
1: "Success"
2: "Oh Yeah"
length: 3
[[Prototype]]: Array(0) */
let p4 = Promise.reject("Error");
var result = Promise.all([p1, p2, p4]);
console.log(result);
/*
[[Prototype]]: Promise
[[PromiseState]]: "rejected"
[[PromiseResult]]: "Error */
race方法
- 第一个完成的promise的状态和结果值就是最终生成的promise对象的状态和结果值
let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
reject("OK");
}, 1000);
});
let p2 = Promise.resolve("Error");
let p3 = Promise.resolve("Oh Yeah");
const result = Promise.race([p1, p2, p3]);
console.log(result); // p2
几个关键问题
如何改变 Promise 的状态
let p = new Promise((resolve, reject) => {
// 1. resolve 函数
resolve("ok"); // pending => fulfilled (resolved)
// 2. reject 函数
reject("error"); // pending => rejected
// 3. 抛出错误
throw "出问题了";
});
指定多个回调函数,是否都会被调用
let p = new Promise((resolve, reject) => {
//如果注释掉resolve(),那么p的状态就还是pending,即状态未发生改变,不会调用then
resolve("OK");
});
// 绑定的这两个回调,只要状态发生了改变就都会被同时调用
p.then((value) => {
console.log(value);
});
p.then((value) => {
console.log(value);
});
改变状态和then函数执行先后
- 谁先谁后都是有可能的,在一般情况下(回调函数内是一个同步任务,直接调用resolve或者reject时)先改变状态再执行then函数,若回调函数内是一个异步任务,则会先执行then函数再改变状态
- 什么时候才能得到数据?
- 如果先指定的回调, 那当状态发生改变时, 回调函数就会调用, 得到数据
- 如果先改变的状态, 那当指定回调时, 回调函数就会调用, 得到数据
Promise .then()返回的新 Promise 的结果状态由什么决定
-
then() 会返回一个新的promise
-
其状态值和结果值由then()指定的回调函数执行结果决定
- 若抛出异常,新promise则为失败,reason为结果值
- 若返回的是非promise的任意值,新promise值会变成成功,value为返回值
- 若返回的是另一个新的promise,该promise的结果是新的promise的结果(是成功新的也为成功,是失败新的也会是失败)
- 若什么回调函数都不返回,其新promise状态值为fullfilled,结果值为undefined
let p = new Promise((resolve, reject) => {
resolve("ok");
// reject("no");
});
//执行 then 方法
let result = p.then((value) => {
// console.log(value);
//1. 抛出错误 rejected
// throw "出了问题";
//2. 返回结果是非 Promise 类型的对象 fulfilled
// return 521;
//3. 返回结果是 Promise 对象 fulfilled/rejected
return new Promise((resolve, reject) => {
// resolve("success");
reject("error");
});
},(reason) => {
console.warn(reason);
})
console.log(result);
Promise 如何串连多个操作任务?
- 由于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 => {
console.log(value); // success
}).then(value => {
// 这里是打印的是结果值,then函数还是会返回一个promise类型
console.log(value); // undefined
})
Promise 异常传透
- 当使用 Promise 的 then 链式调用时,可以在最后指定失败的回调
- 中间任何操作抛出了异常,都会传到最后失败的回调中处理,且出现异常之后的回调都不会再执行
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('OK');
// reject('Err');
}, 1000);
});
p.then(value => {
// console.log(111);
throw '失败啦!';
}).then(value => {
console.log(222);
}).then(value => {
console.log(333);
}).catch(reason => { //用then也可以捕获异常,不过then要传两个参数
console.warn(reason);
});
console.log(p)
如何中断 Promise 链
- 处于pending状态时,状态并未发生改变,then()的回调函数不会被执行,可以在回调函数中返回一个 pendding 状态的 Promise 对象中中断promise链
let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('OK');
}, 1000);
});
let a = p.then(value => {
console.log(111);
// 中断promise链
return new Promise(() => {});
}).then(value => {
console.log(222);
}).then(value => {
console.log(333);
}).catch(reason => {
console.warn(reason);
});
async函数
- 返回值为promise对象
- promise对象的结果由async函数执行的返回值决定(和 then() 规则一样)
- 返回值是一个非promise对象,其状态值为fullfilled,其结果值为return的值
- 返回值如果是一个promise对象,函数返回值则为该对象
- 返回值抛出异常,函数返回状态值为reject,结果值为异常
async function main() {
//1. 如果返回值是一个非Promise类型的数据 则状态为成功,返回一个成功的promise对象
return 521;
}
async function main1() {
//2. 如果返回的是一个Promise对象 则状态取决于返回结果
return new Promise((resolve, reject) => {
// resolve('OK'); //成功
reject("Error"); //失败
});
}
async function main2() {
// 3. 抛出异常 返回一个失败的promise对象
throw "Oh NO";
}
let result = main();
let result1 = main1();
let result3 = main2();
console.log(result);
console.log(result1);
console.log(result3);
await关键字
-
获取状态为成功时的结果值
- 如果是promise对象且状态为成功,返回其成功时的结果值,
- 如果是promise对象状态为失败,抛出错误,且需要通过 try…catch 捕获处理
- 如果是非promise对象,返回关键字后面的值
-
await 必须写在 async 函数中, 但 async 函数中可以没有 await
async function main() {
let p = new Promise((resolve, reject) => {
resolve("OK");
// reject("Error");
});
//1. 右侧为promise的情况
let res = await p;
console.log(res); // ok
//2. 右侧为其他类型的数据
let res2 = await 20;
console.log(res); // 20
//3. 如果promise是失败的状态
try {
let res3 = await p;
console.log(res3)
} catch (e) {
//catch捕获失败状态
console.log(e); // Error
}
}
async函数和await关键字的综合应用
const fs = require('fs');
const util = require('util');
const readFile = util.promisify(fs.readFile);
async function test(){
try{
let data1 = await readFile('./data1.txt');
let data2 = await readFile('./data2.txt');
let data3 = await readFile('./data3.txt');
console.log(data1 + data2 + data3);
}catch(e){
console.log(e);
}
}