Promise 简单介绍与使用
异步编程新的解决方案
- fs 文件操作
- 数据库操作
- AJAX
- 定时器
Promise 的优势
- 支持链式调用
- 便于异常处理
- 指定毁掉
Promise 的简单使用
const p = new Promise((resolve, reject)=>{ // resolve 和 reject 都是函数类型,当任务执行成功是, resolve (解决)就会被调用,当任务执行失败时就会执行 reject (拒绝)
setTimeout(()=>{
if (业务判断是否成功){
resolve() // 调用这个,会将 Promise 对象的状态设置为成功。可以给该函数任意传参,到时候then中的回调就会接收到该参数
}else{
reject() // 调用这个,会将 Promise 对象的状态设置为失败。可以给该函数任意传参,到时候then中的回调就会接收到该参数
}
})
})
// then 接收2个参数,第一个参数是接收成功时候的回调,第二个是接收失败时候的回调
p.then(()=>{
// 当上面的 resolve() 被调用时,这里的代码就会执行。如果上面调用 resolve()时传了参数,这里就可以获取到该参数
},()=>{
// 当上面的 reject() 被调用时,这里的代码就会执行。如果上面调用 reject()时传了参数,这里就可以获取到该参数
})
使用 Promise 封装 AJAX
# 方法1
function ajax(url, data = {}, type = "GET") {
return new Promise((resolve, reject) => {
$.ajax({
type: type,
url: url,
data: data,
dataType: "json",
success: function(data) {
resolve(data);
},
error: function(err) {
reject(err);
}
})
})
}
let url = "http://localhost:5000/login";
let data = {
username: "admin",
password: "admin"
}
let promise = ajax(url, data, "GET"); //注意这里返回的是promise对象
promise.then(data = > { //f1为第一个回调处理函数
f1(data);
}).then(data2 = > { //f2为第二个回调处理函数
f2(data2);
}).catch (err = > {
console.log(err);
})
# 方法2
function ajax(url, data = {}, requestType = "GET") {
return new Promise(function(resolve, reject) {
const xhr = new XMLHttpRequest();
xhr.open(requestType, url); // 定义请求方式
xhr.responseType = 'json'; // 定义返回值类型
xhr.onload = function() {
if(this.status === 200) {
resolve(this.response);
} else {
reject(new Error(this.statusText));
}
}
// 或以下这段也行
// xhr.onreadystatechange = function() {
// if (xhr.readyState===4){
// if(xhr.status >=200 && xhr.status <300){
// resolve(xhr.response);
// }else{
// reject(xhr.status);
// }
// }
// }
xhr.send(data); // 如果没数据就可以不用传参
})
}
ajax('/api/xxx.json').then((value)=> { // value 就是自定义的形参名,可以写成任意字符
console.log(value);
return ajax('api/yyy.json');
}, (error)=> { // error 就是自定义的形参名,可以写成任意字符,他能捕获调用ajax('/api/xxx.json')时的异常,但不能捕获ajax('api/yyy.json')调用时的异常,而catch可以
console.log(error);
}).catch(function(error) { // 更像是给整个promise链条注册的失败回调,建议都是用这种情况,更方便,也更符合链式调用特性
console.log(error);
})
Promise 的状态
Promise 的实例对象中有一个属性 PromiseState ,它有3个值
- pending: 未决定的
- resolve/fullfilled: 成功
- reject: 失败
Promise 对象的值
Promise 的实例对象中有一个属性 PromiseResult 保存着该 Promise 对象成功或失败的结果
Promise API
Promise 的构造函数
Promise 的构造函数 Promise(executor){}
- executor 函数: 执行器 (resolve, reject)={},executor会在 Promise 内部立即同步调用,异步操作在执行器中执行。也就是 (resolve, reject)=
all()
在多个 Promise 执行成功后,这个 Promise 对象才变为成功状态
let p1 = new Promise((resolve,reject)=>{
resolve("OK")
})
let p1 = Promise.resolve("success")
let p2 = Promise.resolve("success2")
const result = Promise.all([p1,p2,p3]);
console.log(result); // 因为上面的3个 Promise 都调用了 resolve,所以输出的 promise 对象的状态是 fullfilled,PromiseResult 保存着上面的执行成功的结果 ["OK","success","success2"]
// 如果其中一个 Promise 调用的是 reject("error111") ,则 result 的 PromiseResult 就是 "error111"
race()
在多个 Promise 执行时,第一个执行完成的 promise 的结果就是最终结果
let p1 = new Promise((resolve,reject)=>{
resolve("OK")
})
let p1 = Promise.resolve("success")
let p2 = Promise.resolve("success2")
const result = Promise.race([p1,p2,p3]);
console.log(result); // 因为上面的3个 Promise 那个先执行 resolve 或 reject ,所以输出的 result 对象的状态就是这个结果
Promise 关键问题
修改 Promise 状态
let p1 = new Promise((resolve,reject)=>{
// resolve("OK") 方法1
// reject("OK") 方法2
// throw '出问题了' 方法3
})
在 then() 中修改 Promise 状态
let p1 = new Promise((resolve,reject)=>{
// resolve("OK") 方法1
// reject("OK") 方法2
// throw '出问题了' 方法3
})
let result = p1.then(value=>{
// 如果抛出错误,那么 result 对象的结果就是失败
// throw '出了问题'
// 如果返回结果是非 Promise 对象,则 result 对象的结果就是成功,它的结果就是返回的数据
// return 123
// 如果返回结果是 Promise 对象,那么 result 对象的结果和值取决于返回的这个 Promise 对象的结果和值
// return new Promise((resolve,reject)=>{
// resolve("OK")
// })
})
多个 Promise 任务串联执行
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)
}).then(value=>{
console.log(value)
})
// 执行结果输出的是"success" undefined,因为第一个 Promise 使用了 setTimeout,所以在执行第一个 then 时 new Promise 的结果已经将其覆盖了,所以第二个 then 的 value 就是第一个 then return 的 Promise 的结果,又因为第二个 then 没有返回任何值,所以第三个 then 的 value 就是 undefined
Promise 异常穿透
let p = new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve("OK");
},1000)
})
p.then(value=>{
throw '失败啦'
}).then(value=>{
console.log(222)
}).then(value=>{
console.log(333)
}).catch(reason=>{
console.warn(reason)
})
// 执行结果输出的是'失败啦',而不会输出 222、333
中断 Promise 调用链
let p = new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve("OK");
},1000)
})
p.then(value=>{
console.log(111)
}).then(value=>{
console.log(222)
return new Promise((resolve,reject)=>{}) # 调用一个空的即可中断之后的 then 的执行。且只有这一个方式可以正常中断调用链
}).then(value=>{
console.log(333)
}).catch(reason=>{
console.warn(reason)
})
// 输出 111、222
Promise 自定义封装
自己简单实现 Promise
原理理解版:
function Promise(executor){
// 添加属性
this.PromiseState = 'pending';
this.PromiseResult = null;
this.callbacks = [];
const self = this;
// resolve 函数
function resolve(data){
if (self.PromiseState !== 'pending') return; // 要保证状态只能被改一次
this.PromiseState = 'fullfilled';
this.PromiseResult = data;
setTimeout(()=>{
self.callbacks.forEach(item=>{
item.onResolved(data);
})
});
}
// reject 函数
function reject(data){
if (self.PromiseState !== 'pending') return;// 要保证状态只能被改一次
this.PromiseState = 'rejected';
this.PromiseResult = data;
setTimeout(()=>{
self.callbacks.forEach(item=>{
item.onRejected(data);
})
});
}
try{
// 同步调用执行器函数 executor
executor(resolve, reject)
}catch(e){
reject(e) // throw "报错内容",的"报错内容"就会传给 e,然后 e 再传给 reject
}
}
// 添加 then 方法
Promise.prototype.then = function(onResolved,onRejected){
const self = this;
// 判断回调函数参数
if (typeof onRejected !== 'function'){
onRejected = reason=>{
throw reason;
}
}
if (typeof onResolved !== 'function'){
onResolved = value=>value;
// onResolved = value=>{return value}; // 等同于上面那句
}
return new Promise((resolve, reject)=>{
// 封装函数
function callback(type){
try{
// 获取函数的执行结果
let result = type(self.PromiseResult);
if (result instanceof Promise){
result.then(v=>{
resolve(v);
},r=>{
resolve(r);
})
}else{
// 结果的对象状态为成功
resolve(result);
}
}catch(e){
reject(e);
}
}
if(this.PromiseState === 'fullfilled'){
setTimeout(()=>{
callback(onResolved);
})
}
if(this.PromiseState === 'rejected'){
setTimeout(()=>{
callback(onRejected);
})
}
if(this.PromiseState === 'pending'){
// 保存回调函数
this.callbacks.push({onResolved: function(){
callback(onResolved);
},onRejected: function(){
callback(onRejected);
}});
}
})
}
// 添加 catch 方法
Promise.prototype.catch = function(onRejected){
return this.then(undefined, onRejected);
}
// 添加 resolve 方法
Promise.resolve = function(value){
// 返回 promise 对象
return new Promise((resolve,reject)=>{
if (value instanceof Promise){
value.then(v=>{
resolve(v);
},r=>{
reject(r);
})
}else{
resolve(value);
}
});
}
// 添加 rejected 方法
Promise.rejected = function(reason){
// 返回 promise 对象
return new Promise((resolve,reject)=>{
reject(reason);
});
}
// 添加 all 方法
Promise.all = function(promise){
// 返回结果为 promise 对象
return new Promise((resolve,reject)=>{
let count = 0;
let arr = []; // 准备个数组存放所有 promise 的执行结果
for(let i=0;i<promise.length;i++){
promise[i].then(v=>{
// 得知所有对象状态是成功的,才调用resolve
count++;
arr[i] = v;
if (count === promise.length){
resolve(arr);
}
},r=>{
reject(r);
},)
}
});
}
// 添加 race 方法
Promise.race = function(promise){
// 返回结果为 promise 对象
return new Promise((resolve,reject)=>{
for(let i=0;i<promise.length;i++){
promise[i].then(v=>{
resolve(v);
},r=>{
reject(r);
},)
}
});
}
class 版
class Promise{
// 构造方法
constructor(executor){
// 添加属性
this.PromiseState = 'pending';
this.PromiseResult = null;
this.callbacks = [];
const self = this;
// resolve 函数
function resolve(data){
if (self.PromiseState !== 'pending') return; // 要保证状态只能被改一次
this.PromiseState = 'fullfilled';
this.PromiseResult = data;
setTimeout(()=>{
self.callbacks.forEach(item=>{
item.onResolved(data);
})
});
}
// reject 函数
function reject(data){
if (self.PromiseState !== 'pending') return;// 要保证状态只能被改一次
this.PromiseState = 'rejected';
this.PromiseResult = data;
setTimeout(()=>{
self.callbacks.forEach(item=>{
item.onRejected(data);
})
});
}
try{
// 同步调用执行器函数 executor
executor(resolve, reject)
}catch(e){
reject(e) // throw "报错内容",的"报错内容"就会传给 e,然后 e 再传给 reject
}
}
// then 方法封装
then(onResolved,onRejected){
const self = this;
// 判断回调函数参数
if (typeof onRejected !== 'function'){
onRejected = reason=>{
throw reason;
}
}
if (typeof onResolved !== 'function'){
onResolved = value=>value;
// onResolved = value=>{return value}; // 等同于上面那句
}
return new Promise((resolve, reject)=>{
// 封装函数
function callback(type){
try{
// 获取函数的执行结果
let result = type(self.PromiseResult);
if (result instanceof Promise){
result.then(v=>{
resolve(v);
},r=>{
resolve(r);
})
}else{
// 结果的对象状态为成功
resolve(result);
}
}catch(e){
reject(e);
}
}
if(this.PromiseState === 'fullfilled'){
setTimeout(()=>{
callback(onResolved);
})
}
if(this.PromiseState === 'rejected'){
setTimeout(()=>{
callback(onRejected);
})
}
if(this.PromiseState === 'pending'){
// 保存回调函数
this.callbacks.push({onResolved: function(){
callback(onResolved);
},onRejected: function(){
callback(onRejected);
}});
}
})
}
catch(onRejected){
return this.then(undefined, onRejected);
}
// 属于类,而不属于实例对象
static resolve(value){
// 返回 promise 对象
return new Promise((resolve,reject)=>{
if (value instanceof Promise){
value.then(v=>{
resolve(v);
},r=>{
reject(r);
})
}else{
resolve(value);
}
});
}
// 添加 rejected 方法
static rejected(reason){
// 返回 promise 对象
return new Promise((resolve,reject)=>{
reject(reason);
});
}
// 添加 all 方法
static all(promise){
// 返回结果为 promise 对象
return new Promise((resolve,reject)=>{
let count = 0;
let arr = []; // 准备个数组存放所有 promise 的执行结果
for(let i=0;i<promise.length;i++){
promise[i].then(v=>{
// 得知所有对象状态是成功的,才调用resolve
count++;
arr[i] = v;
if (count === promise.length){
resolve(arr);
}
},r=>{
reject(r);
},)
}
});
}
// 添加 race 方法
static race(promise){
// 返回结果为 promise 对象
return new Promise((resolve,reject)=>{
for(let i=0;i<promise.length;i++){
promise[i].then(v=>{
resolve(v);
},r=>{
reject(r);
},)
}
});
}
}
async 与 await
async 函数
- 函数的返回值为 promise 对象
- promise 对象的结果由 async 函数执行的返回值决定,如果 async 函数返回的是一个 promise 对象,那么 async 的结果的 promise 对象就是 async 函数中返回的 promise 结果和状态
async function main(){
}
let result = main();
console.log(result); // 输出发现是个 promise 对象
await 表达式
- await 右侧的表达式一般为 promise 对象,但也可以是其他值,比如数字、字符串等
- 如果表达式是 promise 对象,await 返回的是 promise 成功的值
- 如果表达式是其他值,则将此值作为 await 的返回值
async function main(){
// 1. 右侧为 promise 的情况
let p = new Promise((resolve,reject)=>{
resolve("OK");
})
let res = await p; // 会返回 promise 成功的值,也就是 OK
console.log(res);
// 2. 右侧为其他数据的情况
let res2 = await 20; // await 右侧是其他值时,是什么值就返回什么值
console.log(res2);
// 3. 右侧为 promise 失败的情况
let p3 = new Promise((resolve,reject)=>{
reject("error, error");
})
try{
let res3 = await p3; // 就会抛出一个错误,需要使用 try...catch 捕获
console.log(res3);
}catch(e){
console.warn(e);
}
}
async 与 await 需要注意的地方
- await 必须写在 async 函数中,但 async 中可以没有 await
- 如果 await 的 promise 失败了,就会抛出异常,需要通过 try…catch 捕获处理
async 与 await 结合使用
// 比如要读取 1.html、2.html、3.html 这3个文件
// 传统写法
const fs = require('fs');
fs.readFile('./resource/1.html',(err,data1)={
if(err) throw err;
fs.readFile('./resource/2.html',(err,data2)={
if(err) throw err;
fs.readFile('./resource/3.html',(err,data3)={
if(err) throw err;
console.log(data1+data2+data3);
})
})
})
const util = require('util');
const mineReadFile = util.promisify(fs.readFile); // promisify 方法,可以将传进去的API转变成 promise 形式的函数
// 使用 async 与 await 方式
async function main()
{
try{
// 读取第一个文件内容
let data1 = await mineReadFile('./resource/1.html')
let data2 = await mineReadFile('./resource/2.html')
let data3 = await mineReadFile('./resource/3.html')
console.log(data1+data2+data3);
}catch(e){
console.log(e)
}
}
Q.E.D.