1、什么是promise
以前,对于JavaScript中传统的异步编程,一般会有两种解决方案:回调函数、发送事件。但是如果流程和数据传递比较复杂,回调就会尤为冗长和复杂,所以为了解决异步编程问题而产生了promise,即promise是ES6新增的为了解决异步(非阻塞)中存在的问题而产生的构造函数。
2、promise中的三种状态
本质上来说,promise是一个对象,它持有异步操作的状态,用来表示异步操作的不同结果。其遵循一下两个原则:
(1)promise对象只有如下三种状态,其状态是由操作结果决定的,而且是没有其他方式更改的。
【1】pending— —进行中;
【2】fulfilled(resolved)— —已完成(已成功);
【3】rejected— —已失败;
【注意】要将已完成,已失败两种状态与 使状态发生改变的resolve和reject两种方法区分开来。
(2)状态的改变只有两种情况:
【1】从pending 变为 fullfilled;
【2】从pending 变为 rejected。
也就是可以理解为promise中的异步操作是**只能从进行到结束,**而且一旦结束,结果就不会再改变。不能暂停,不能取消,不能改变已经产生的结果。
【注意】要与事件监听机制的错过事件后就得不到结果的特性区分开来,如果 Promise 异步操作已经结束,状态已经改变,那在此之后的任何时候依然是可以通过对 Promise 添加回调函数获取到这个结果的。
Promise.prototype.then : 当promise的状态返回resove时,则调用then()方法
Promise.prototype.catch :当promise的状态返回reject时,则调用catch()方法。
但是,如果promise异步操作中其状态仍处于pending而未发生改变是,外部对promise的具体阶段是不可见的,也不能手动取消该异步操作。
3、使用promise
(1)构建promise的格式
new Promise(function(resolve,reject){
处理语句;
if(成功){
resolve();
}
else{
//失败
reject();
}
}).then(function(){
成功时执行语句;
}).catch(function(){
失败时执行语句; })
(2)promise的使用步骤具体如下所示:
【1】使用new关键字创建promise
由于promise是一个构造函数,故用new关键字创建一个实例,创建时接受一个函数,该函数有两个函数类型的参数(resolve,reject),具体如下所示。
const promise = new Promise((resolve,reject)=>{...})
resolve和reject两个函数参数决定了当前promise的状态改变以及**异步操作的结果处理方法,**他们都可以将异步操作的结果传递出去。
【2】resolve和reject传递异步操作结果
一般而言,resolve用于传递异步操作成功的结果;reject用于传递异步操作失败时的结果或者是出现的异常。
当调用resolve时,promise的状态就由pending变为fullfilled;当调用reject时,promise状态由pending变为rejected。具体如下代码所示:
//用 getListData() 表示请求网络数据,返回值就是请求结果。
const promise = new Promise(resolve,reject=>{ const res= getListData();
//根据状态码判断成功失败,调用 resolve 和 reject
if(res && res.status===200){ resolve(res); }else{ reject(res); }})
【3】then()方法指定回调
promise对象实例生成,执行对应的异步操作获取到结果之后,可以用then方法指定不同状态的回调。
then方法接收**两个回调函数作为参数:**第一个回调函数(resolve)处理resolved状态,第二个回调函数(reject)处理rejected状态(此参数为可选的)。
具体可看以下代码例子:
// 创建一个 Promise// 指定时间后, reject 传递 “exception”
function timeout(ms) {
return new Promise((resolve, reject) => {
setTimeout(reject, ms, "exception");
//调用了reject,触发了then方法的第二个回调
});}
timeout(1000).then(
(value) => { console.info("value", value); },
(err) => { console.info("err", err); });
Promise 中 调用reject,触发了then 方法的第二个回调,故结果为:
err exception
若setTimeout 中指定的函数是 resolve ,就触发了then 方法的第一个回调,运行结果则变为:
value exception
(3)需注意的地方
【1】promise创建时立即执行
new Promise((resolve, reject) => {
console.info("start");
setTimeout(() => {
console.info("time");
resolve("data"); }, 1000);
}).then(
(res) => console.info("then-resolve"),
(err) => console.info("then-reject"));console.info("end");
注意上述代码:用 setTimeout 模拟异步。虽然“start” 在 Promise 内部,但是并不在 setTimeout 调用函数里面,因为Promise创建时立即执行,所以 ”start“ 最先输出的,然后执行异步操作(setTimeout),此时“end” 会被输出。一秒后调用 setTimeout 的函数,输出 “time”,然后 resolve 被调用,then 方法的第一个回调函数触发,输出 “then-resolve” 。
其输出结果顺序如下所示:
startendtimethen-resolve
【2】resolve 和 reject 不会终止 Promise 参数函数执行
对普通函数,通过 return 输出返回值之后,函数执行也会终止。但是创建 Promise 时提供的回调函数的两个参数 resolve 和 reject在调用传递值之后并不会终止函数的执行。
new Promise((resolve, reject) => {
setTimeout(() => {
console.info("start");
resolve("data");
console.info("end");
//end依然能够被输出
}, 1000);
}).then(
(res) => console.info("then-resolve"),
(err) => console.info("then-reject"));
输出结果是:
startendthen-resolve
如上输出结果所示resolve后面的“end”依然能够被输出,原因在于resolve和reject的执行是晚于本轮事件循环的其他同步任务的。
resolve和reject传递值之后,promise异步操作的目的已经达到,其他操作都应该到then方法中进行,所以最好不要在resolve语句后面再添加其他操作。当然,可以在resolve,reject语句前面加上return结束回调函数执行。
4、其他promise API的使用(部分API)
(1)第一步的异步结果获取到之后**还有其他操作 (同步或异步)**时——继续调用then方法:
【1】 其实 then 方法是会返回一个新的 Promise 对象的,所以也就可以链式调用then 方法,
new Promise((resolve, reject) => {
resolve("33");}) .then((res) => {
return "result" + res;
}) .then((res) => console.log(res));
以上代码的结果为:
result:33
【注】then 方法连续调用时,第一个 then 方法的回调函数里 return 的数据会作为后一个 then 方法的回调函数的参数。
【2】前一个 then 方法的回调函数 return 另一个 Promise 对象的时,后一个 then 方法等待该 Promise 对象的状态改变,再执行对应的回调函数(resolved 对应then方法中的第一个回调,rejected 对应第二个回调)。
new Promise((resolve, reject) => { resolve("33");}) .then((res) => { return new Promise((resolve, reject) => { setTimeout(() => { resolve("result:" + res); }, 1000); }); }) .then((res) => console.log(res));
结果一秒后输出:
result:33
(2)catch()
catch 可以用来处理异步操作结果失败或者发生出错的情况。你可能发现 reject 也可以用来处理这些情况,其实,catch 本就是一个对 then 中的 reject回调的补充,但更为简洁。
const pro = new Promise((resolve, reject) => {
reject("100");});
//方法1
pro.then(
(res) => console.info("res", res),
(err) => console.info("err", err));
//方法2
pro.then(
(res) => console.info("res", res)).catch((e) => console.info("e", e));
以上两种方法是等价的。
【总结】
【1】catch 和 then 方法的第二个回调都可以捕获错误。
【2】Promise 对象的错误会向后传递直到被捕获,同一个错误一旦被捕获就不能再次被捕获。
【3】常用的 promise.then().catch() 中, catch 可以捕获对应 then 中的错误,因而更常用。
【4】catch 方法的返回值依然是 Promise,可以继续链式调用then和 catch。
(3)finally()
通过 then 的两个回调函数对 Promise 两种状态分别进行处理,有时候也会有一些公共的操作。比如,不管 Promise 状态成功或者失败,希望都能手动刷新一次页面,那么这个操作就应该放在 finally 中。(其实,finally 只是对 then 方法的两个回调函数中的一些公共操作的一次抽取,避免了成功和失败情况的相同操作写两次。)
finally表示不管 Promise 最后的状态,在执行 then 或者 catch 的回调函数之后,都会执行 finally 中的回调函数。
const pro = new Promise((resolve, reject) => {
resolve("aaa");});pro.then(
() => {},
() => {} ).finally(() => {updateUI()});
【注意】因为 finally 的回调函数是没有参数的,意味着finally中进行的操作只能是状态无关的,和 Promise 状态相关的操作是不能抽取到这里的。
[上述知识点还有待继续整理补充]...
参考:
【1】es6.ruanyifeng.com/#docs/promi…
等等