目录
一、什么是Promise
1.1 Promise的三种状态
二、Promise 基本用法
2.1 Promise基本使用
2.2 Promise使用时传参
2.3 Promise 链式调用
2.4 链式调用注意事项
三、Promise内置方法
3.1 Promise.all()
3.2 Promise.race()
3.3 Promise.allSettled()
3.4 Promise.finally()
3.5 Promise.any()
一、什么是Promise
A Promise is an object representing the eventual completion or failure of an asynchronous operation.
从官方文档我们可知
- Promise是ES6新增的一个对象,一个构造函数。
- 用于多层次异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数地狱问题。
promise主要是用来解决回调地狱的问题,通过使用.then来使得代码成链式调用,方便维护和使用。.then中的回调函数属于异步任务中的微任务。
1.1 Promise的三种状态
- pending: 初始状态,既不是成功,也不是失败状态。
- fulfilled: 意味着操作成功完成。——注:fulfilled 或称 resolved
- rejected: 意味着操作失败。
什么意思呢?三种状态对应异步操作的三种情况,比如发送一次ajax请求,初始等待时,状态为pending;ajax请求成功时,调用Promise内置函数resolve(), Promise状态 => fulfilled;请求失败时,调用函数reject(),Promise状态 => rejected。
Promise是异步编程的一种解决方案,它的构造函数是同步执行的,then 方法是异步执行的,所以Promise创建后里面的函数会立即执行,构造函数中的resolve和reject只有第一次执行有效,也就是说Promise状态一旦改变就不能再变
二、Promise 基本用法
var Promise: PromiseConstructor
new <any>(executor: (resolve: (value?: any) => void, reject: (reason?: any) => void) => void) => Promise<any>
从语法上来看,Promise 是一个构造函数,需要传入一个参数 executor函数。
new Promise 时会调用 executor 函数, executor 函数也有两个参数 resolve 和 reject,这两个参数也是函数,调用 resolve 或 reject 时,分别将promise的状态改为fulfilled(完成)或 rejected(失败)。Promise 状态只能唯一。
2.1 Promise基本使用
下面来看两个例子
(1) 用流程控制的分支结构,判断异步请求是否成功
var promise = new Promise((resolve, reject) => {
if(异步请求成功){
resolve()
}else{
reject()
}
})
promise.then(()=>{
//success
},()=>{
//failure
})
(2) 用定时器setTimeout模拟异步请求成功
function ajax() {
return new Promise(resolve => {
setTimeout(resolve, 1000)
})
}
ajax().then(() => {
console.log("request success") //success
})
上面代码表示,如果异步操作成功,就调用resolve()方法,就会执行Promise实例的then()方法的第一个回调函数,如果失败则调用Promise实例的then()方法的第二个回调函数。
2.2 Promise使用时传参
resolve() 和 reject() 函数调用时可传参,传入的参数会被Promise实例的 then 和 catch 方法捕获。
var promise = new Promise((resolve, reject) => {
if (异步请求成功) {
resolve("success")
} else {
reject("error")
}
})
promise.then( res => {
res // success
})
//捕获异常可用catch()方法
promise.catch( err => {
err // error
})
我们来执行一下,看看结果:
2.3 Promise 链式调用
Promise强大的地方在于此,如果发送一个异步请求,又返回另外一个异步请求时,可用链式调用
new Promise((resolve) => {
resolve(1)
}).then((res) => {
return new Promise((resolve) => {
resolve(res+2)
})
}).then((res) => {
return new Promise((resolve) => {
resolve(res+3)
})
}).then((res) => {
return res // 6
})
当对异步请求返回结果 res 的简单操作时,可用 Promise.resolve() 简写
new Promise((resolve) => {
resolve(1)
}).then((res) => {
return Promise.resolve(res+2)
}).then((res) => {
return Promise.resolve(res+3)
}).then((res) => {
return res // 6
})
还有一种更简单的语法糖
new Promise((resolve) => {
resolve(1)
}).then((res) => {
return res + 2
}).then((res) => {
return res + 3
}).then((res) => {
return res // 6
})
运行结果:
2.4 链式调用注意事项
链式调用(chaining)按序执行,有以下约定,使用时要多注意。
- 在本轮 事件循环运行完成之前,回调函数是不会被调用的。
- 即使异步操作已经完成(成功或失败),在这之后通过 then()添加的回调函数也会被调用
- 通过多次调用then() 可以添加多个回调函数,它们会按照插入顺序执行
看下面的例子
new Promise((resolve) => {
resolve()
}).then(() => {
console.log('execute') // execute
}).then(() => {
console.log('execute1') // execute
}).then(() => {
console.log('execute2') // execute
})
我们来看看,运行结果:
只要触发了一次resolve(),链上的所有then都会被调用,当然后面的没有调用resolve自然拿不到操作数。
new Promise((resolve) => {
resolve()
}).then(() => {
console.log('execute') // execute
}).then(() => {
console.log('execute1') // execute
}).catch(()=>{
console.log('execute2') // no execute
}).then(()=>{
console.log('execute3') // execute
})
运行结果:
注:中间穿插catch(),其后的then也会被执行
new Promise((resolve,reject) => {
reject()
}).then(() => {
console.log('execute') // no execute
}).catch(()=>{
console.log('execute1') // execute
}).then(() => {
console.log('execute2') // execute
})
运行结果:
注:捕获reject()后的then会被执行
触发resolve()之后,第一个then()执行,输出execute,接着抛出一个错误对象,中断程序的执行,接着catch()捕获错误信息对象(第二个then()不会被执行),即catch()被执行,输出execute2。—— 注:throw new Error(error),创造一个错误类型实例抛出;
关于throw new Error(error) 与 throw error
① throw new Error(error),是创建错误,创造一个错误类型抛出;
②throw error,这个是抛出错误。(不建议的写法)
throw语句的作用是手动中断程序执行,抛出一个错误。抛出错误一般都是与try catch 同时出现的。
注意,throw语句会中断程序的执行,导致throw语句后的语句无法正常执行
③ throw可以抛出任何类型的值,如: throw 42;
throw可以抛出任何类型的值,不仅仅是new Error() ,即throw new Error(),也就是说,它的参数可以是任何值。
new Promise((resolve) => {
resolve()
}).then(() => {
console.log("execute") // execute
throw new Error()
}).then(() => {
console.log("execute1") // no execute
}).catch(() => {
console.log("execute2") // execute
}).then(() => {
console.log("execute3") // execute
})
运行结果:
情况有很多种,怎么去理解呢,看下面这个例子
const arr = ["foo","bar"]
arr.forEach(async (item) => {
const res = await new Promise(resolve=>{
resolve("why")
}).then(res=>{
return res
})
console.log(res);
console.log(item);
// why、foo、why、bar
})
运行结果:
res 会拿到 Promise resolve()的操作数,输出结果为 why、foo、why、bar
把 return res 注释发现,仍然可以输出结果,相当于执行了 Promise.resolve(),catch()中同理
const arr = ["foo","bar"]
arr.forEach(async (item) => {
const res = await new Promise(resolve=>{
resolve("why")
}).then(res=>{
// return res
// Promise.resolve()
})
console.log(res); // return res 注释掉,const res无返回值,自然就是undefined
console.log(item);
// undefined、foo、undefined、bar
})
总结:then(),catch()触发后,会返回一个空的 resolve()
三、Promise内置方法
3.1 Promise.all()
Promise 的 all 方法提供了并行执行异步操作的能力,并且在所有异步操作执行完后才执行回调。
Promise.all(iterable) 方法返回一个 Promise 实例,此实例在 iterable 迭代器 参数内所有的 promise 都“完成(resolved)”或参数中不包含 promise 时回调完成(resolve);如果参数中 promise 有一个失败(rejected),此实例回调失败(reject),失败的原因是第一个失败 promise 的结果。
—— 全部fulfilled状态
var p1 = Promise.resolve("res1")
var p2 = Promise.resolve("res2")
Promise.all([p1, p2]).then((res) => {
return res // ["res1", "res2"]
})
运行结果:
—— 有rejected状态时
var p1 = Promise.reject("res1")
var p2 = Promise.resolve("res2")
Promise.all([p1, p2]).then((res) => {
return res // res1
})
运行结果:
—— 捕获异常结果err为响应速度快的
var p1 = new Promise((resolve, reject) => {
setTimeout(() => {
reject("err1")
}, 1000)
})
var p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject("err2")
}, 2000)
})
Promise.all([p1, p2]).catch((err) => {
return err // err1
})
运行结果:
3.2 Promise.race()
与Promise.all()对应,接受的也是数组,里面(数据项)也都是Promise实例。
Promise.race([p1, p2, p3])里面哪个结果获得的快,就返回哪个结果,不管结果本身是成功状态还是失败状态。
1、只要有一个决议为成功或失败,新得到的Promise实例就相应地返回成功或失败,并把值传递过来。也就是说看决议哪个速度快,就返回的是谁
2、all传空数组会立刻决议为成功;而race传空数组会被挂起,它会没有任何反应
- race 函数返回一个 Promise,它将与第一个传递的 promise 相同的完成方式被完成。它可以是完成( resolves),也可以是失败(rejects),这要取决于第一个完成的方式是两个中的哪个。
- 如果传的迭代是空的,则返回的 promise 将永远等待。
- 如果迭代包含一个或多个非承诺值和/或已解决/拒绝的承诺,则 Promise.race 将解析为迭代中找到的第一个值。
示例1:
var p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("res1")
}, 3000)
})
var p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject("err2")
}, 2000)
})
Promise.race([p1, p2])
.then((res)=>{
console.log(res);
}).catch((err)=>{
console.log(err); //err2
})
运行结果:
注意:then,catch只会调用二者其一,并且取决于迭代器中参数的响应速度
示例2:
function getData1() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('第一条数据加载成功');
reject('err');
}, 500);
});
}
function getData2() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('第二条数据加载成功');
resolve('data2');
}, 1000);
});
}
function getData3() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('第三条数据加载成功');
resolve('data3');
}, 1000);
});
}
let p = Promise.race([getData1(),getData2(),getData3()]);
p.then(data => {
console.log(data);
}, e => {
console.log(e);
})
/*
第一条数据加载成功
err
第二条数据加载成功
第三条数据加载成功
*/
运行结果,如下动图演示:
Promise.race([Promise实例1,Promise实例2,Promise实例3]) 里面哪个结果获得的快,就返回哪个结果,不管结果本身是成功状态还是失败状态。
本例中getData1中的Promise实例1获得结果最快,所以race 函数返回一个 Promise实例的结果是getData1的结果,即err。
我的疑问???:race()函数最终返回的Promise实例的Promise Result值为什么是undefined,不应该是 err 吗?待解决......
网上查了一下,关于 [[PromiseResult]] : undefined 的问题,以下是给出的解决方案(不过,好像还是解决不了):
报错信息
[[PromiseResult]] : undefined
通常出现在JavaScript中处理Promise时。这个错误表明有一个Promise对象,但是它的结果属性[[PromiseResult]]
未定义。这个问题可能是因为:
你尝试获取一个尚未解决(fulfilled)的Promise的结果。
你可能在Promise内部抛出了一个错误,但是没有使用
.catch()
来处理这个错误,导致错误没有被捕获,并且Promise的状态变为未解决。解决方法:
确保在尝试获取Promise结果之前,Promise已经被解决(即它的状态变为fulfilled)。
如果你在Promise中捕获错误,请确保你有一个
.catch()
来处理它们,或者你使用的是try/catch
来处理可能抛出错误的代码。使用
Promise.resolve()
或Promise.reject()
来创建一个已解决或已拒绝的Promise,从而避免[[PromiseResult]]
是未定义的情况。如果你在使用异步函数,请确保你正确地使用了
await
关键字来等待Promise解决。示例代码:
// 确保Promise已解决 let promise = new Promise((resolve, reject) => { resolve('Promise resolved!'); }); promise.then((result) => { console.log(result); // 输出: 'Promise resolved!' }).catch((error) => { console.error(error); // 处理错误 }); // 使用try/catch处理可能的错误 async function asyncFunction() { try { let result = await promise; console.log(result); } catch (error) { console.error(error); } }
以上示例展示了如何处理Promise的结果,以及如何捕获并处理潜在的错误。
总结:race: 竞技/竞赛
Promise.race() 接受多个Promise对象 数组形式传递参数
只要有一个Promise状态改变, 那么就结束
以先拿到结果Promise值作为整个值返回
如果最先返回的值状态是fulfilled,那就进入then中结束
如果最先返回的值状态是rejected,那就进入catch中结束
—— 全部fulfilled状态
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(11111)
}, 3000);
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(22222) //最先结束,成为结果
}, 500);
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(33333)
}, 1000);
})
Promise.race([p1, p2, p3]).then(res => {
console.log("res:", res) //res: 22222
}).catch(err => {
console.log("err:", err)
})
—— 有rejected状态时
const p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(11111)
}, 3000);
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject(22222) // 错误状态,成为结果
}, 500);
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(33333)
}, 1000);
})
// race: 竞技/竞赛
// 只要有一个Promise变成fulfilled状态, 那么就结束
Promise.race([p1, p2, p3]).then(res => {
console.log("res:", res)
}).catch(err => {
console.log("err:", err) //err: 22222
})
3.3 Promise.allSettled()
Promise.allSettled()方法返回一个在所有给定的promise已被决议或被拒绝后决议的promise,并带有一个对象数组,每个对象表示对应的promise结果。
示例1:
var p1 = Promise.resolve("res1")
var p2 = Promise.reject("res2")
Promise.allSettled([p1, p2]).then((res) => {
return res // [{status: "fulfilled", value: "res1"}, {status: "rejected", reason: "res2"}]
})
返回一个对象数组,包含iterator所有参数Promise的状态和结果。
运行结果:
该方法参数也是和 .all 相同。顾名思义,这个方法是等所有promise参数确定状态后,才会执行回调函数,不管是成功的状态还是拒绝的状态,都等待全部执行后,并返回一个包含每个 Promise 解决状态的对象数组,每个对象包含两个属性:status 和 value;state表示promise的状态:resolve和rejected,value代表的是promise传递的值。
请注意,Promise.allSettled 是 ES2020(也称为 ES11)中引入的新方法,需要支持该版本的 JavaScript 运行环境才能使用
示例2:
var p1 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log("----打印:p1");
resolve("p1--3000");
}, 3000);
});
var p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject("p2--1000");
}, 1000);
});
var p3 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("p3--500");
}, 500);
});
var p4 = new Promise((resolve, reject) => {
throw new Error("抛出错误");
});
Promise.allSettled([p1, p2, p3, p4])
.then((result) => {
console.log("----打印:result", result);
})
.catch((err) => {
console.log("----打印:", err); //不执行
});
//执行结果
// ----打印:p1
// ----打印:result [
// { status: 'fulfilled', value: 'p1--3000' },
// { status: 'rejected', reason: 'p2--1000' },
// { status: 'fulfilled', value: 'p3--500' },
// {
// status: 'rejected',
// reason: Error: 抛出错误
// }
// ]
运行结果:
3.4 Promise.finally()
Promise.finally方法的回调函数不接受任何参数,这表明,finally方法里面的操作,应该是与Promise状态无关的,无论 Promise 的状态如何,onFinally 回调都会被执行。它不接收任何参数,也没有返回值。这意味着它主要用于清理和最终处理逻辑,而不关心 Promise 的解决结果或拒绝原因。
var p1 = new Promise((resoleve, reject) => {
setTimeout(() => {
resoleve("p1--3000");
}, 3000);
});
p1.then((res) => {
console.log("----打印:", res);
}).finally(() => {
console.log("----打印:调用了");
});
//执行结果
// ----打印: p1--3000
// ----打印:调用了
var p2 = new Promise((resoleve, reject) => {
setTimeout(() => {
reject("p2--1000");
}, 1000);
});
p2.then((res) => {})
.catch((err) => {
console.log("----打印:", err);
})
.finally(() => {
console.log("----打印:也调用了");
});
// 执行结果
// ----打印: p2--1000
// ----打印:也调用了
运行结果:
3.5 Promise.any()
Promise.any接收一个promise的数组作为参数,只要其中有一个Promise成功执行,就会返回已经成功执行的Promise的结果;若全部为rejected状态,则会到最后的promise执行完,全部的promise返回到异常函数中;可用于多通道获取数据,谁先获取就执行下一步程序,跳出这个过程。这和all的相反。
var p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("p1--3000");
}, 3000);
});
var p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject("p2--1000");
}, 1000);
});
var p3 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log("----打印p3");
resolve("p3--5000");
}, 5000);
});
var promiseArr = [p1, p2, p3];
console.time("promiseArr");
Promise.any(promiseArr)
.then((res) => {
console.log("res", res); //res [ 'p1--3000', 'p2--1000', 'p3--5000' ]
console.timeEnd("promiseArr"); // promiseArr: 5.020s
})
.catch((err) => console.log(err));
//输出顺序 --虽然p2已经执行完,但是为rejected状态,而any会返回第一个resolve状态的对象
// res p1--3000
// promiseArr: 3.009s
// ----打印p3
//另外一种情况
var p1 = new Promise((resolve, reject) => {
setTimeout(() => {
reject("p1--3000");
}, 3000);
});
var p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject("p2--1000");
}, 1000);
});
var p3 = new Promise((resolve, reject) => {
setTimeout(() => {
console.log("----打印p3");
reject("p3--5000");
}, 5000);
});
var promiseArr = [p1, p2, p3];
console.time("promiseArr");
Promise.any(promiseArr)
.then((res) => {
console.log("res", res); //res [ 'p1--3000', 'p2--1000', 'p3--5000' ]
console.timeEnd("promiseArr"); // promiseArr: 5.020s
})
.catch((err) => console.log(err));
//输出结果 解释--因为p1,2,3都是错误,所以any一直在等有成功的状态,所以知道p3结束后,没有成功的,就走catch那边
// ----打印p3
// [AggregateError: All promises were rejected] {
// [errors]: [ 'p1--3000', 'p2--1000', 'p3--5000' ]
// }
运行结果:
场景一
场景二
参考:Promise详解大全:介绍、九个方法使用和区别、返回值详解 | Promise链式调用解惑
参考资料
—— 整合总结ES6中常用的新特性:promise对象 ——
Promise详解大全:九个方法使用和区别、返回值详解 | Promise链式调用解惑
JS-Promise用法总结 - CSDN博客 | JS中的promise用法详解 | JS的promise用法 - CSDN博客
如何使用 Promise - 学习 Web 开发 | MDN | JavaScript基础之Promise - 知乎
Javascript中Promise的四种常用方法总结_javascript技巧_脚本之家