es6 Promise
一、 promise 的含义
Promise
是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了 Promise
对象。
所谓 Promise
,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise
是一个对象,从它可以获取异步操作的消息。Promise
提供统一的 API,各种异步操作都可以用同样的方法进行处理。
1. promise 的特点
- 对象的状态不受外界影响。Promise 对象代表一个异步操作,有三种状态:
- pending: 初始状态,不是成功或失败状态。
- fulfilled: 意味着操作成功完成。
- rejected: 意味着操作失败。 只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是 Promise 这个名字的由来,它的英语意思就是「承诺」,表示其他手段无法改变。
- 一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise 对象的状态改变,只有两种可能:从 pending 变为
resolved
和从 pending 变为rejected
。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果。就算改变已经发生了,你再对 Promise 对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。
2. promise 优缺点
优点: 有了 Promise 对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise 对象提供统一的接口,使得控制异步操作更加容易。
缺点: 首先,无法取消 Promise,一旦新建它就会立即执行,无法中途取消。其次,如果不设置回调函数,Promise 内部抛出的错误,不会反应到外部。第三,当处于 Pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
二、 Promise 创建
要想创建一个 promise 对象、可以使用 new
来调用 Promise 的构造器来进行实例化。
下面是创建 promise 的步骤:
var promise = new Promise(function (resolve, reject) {
// 异步处理
// 处理结束后、调用resolve 或 reject
});
Promise 构造函数包含一个参数和一个带有 resolve(解析)和 reject(拒绝)两个参数的回调。在回调中执行一些操作(例如异步),如果一切都正常,则调用 resolve,否则调用 reject。
实例
var myFirstPromise = new Promise(function (resolve, reject) {
//当异步代码执行成功时,我们才会调用resolve(...), 当异步代码失败时就会调用reject(...)
//在本例中,我们使用setTimeout(...)来模拟异步代码,实际编码时可能是XHR请求或是HTML5的一些API方法.
setTimeout(function () {
resolve("成功!"); //代码正常执行!
}, 250);
});
myFirstPromise.then(function (successMessage) {
//successMessage的值是上面调用resolve(...)方法传入的值.
//successMessage参数不一定非要是字符串类型,这里只是举个例子
document.write("Yay! " + successMessage);
});
三、 Promise Ajax
//搜索 `海阔天空` ,得到的结果是数组
function p1() {
return new Promise((resolve, reject) => {
$.ajax({
url: `http://netease2.bluej.cn/search`,
data: {
keywords: "海阔天空",
},
success(res) {
console.log("函数里 p1 自己的res", res);
resolve(res); //把 p1 的 结果 给then
},
error(err) {
reject(err); //把 p1 的 报错 给catch
},
});
});
}
四、 Promise then
// p1_res:自定义的变量,存储上面p1的res
function p2(p1_res) {
return new Promise((resolve, reject) => {
//得到数组中的第一首歌
let song = p1_res.result.songs[0];
//得到第一首歌的歌手id
let id = song.artists[0].id;
$.ajax({
url: `http://netease2.bluej.cn/artist/desc`,
data: {
id: id,
},
success(res) {
//获取歌手的详情
console.log("歌手详情", res);
resolve(res); //把 p2 的 结果 留给后面可能用到的then
},
error(err) {
reject(err); //把 p2 的 报错 给catch
},
});
});
}
现在有 p1 和 p2 两个方法,p2 的执行需要等待 p1 的结果
//执行p1 ->then 执行p2 ->then 执行一个函数接收p2的结果并输出
p1()
.then(p2)
.then((p2_res) => {
console.log("p2给的", p2_res);
});
五、 Promise catch
//执行p1 ->then 执行p2 ->then 执行一个函数接收p2的结果并输出
p1()
.then(p2)
.then((p2_res) => {
console.log("p2给的", p2_res);
})
.catch((err) => {
console.log("catch", err);
}); //输出错误信息,如果是p1报错,那么p2就不执行
六、 Promise finally
//执行p1 ->then 执行p2 ->then 执行一个函数接收p2的结果并输出
p1()
.then(p2)
.then((p2_res) => {
console.log("p2给的", p2_res);
})
.catch((err) => {
console.log("catch", err);
}) //输出错误信息,如果是p1报错,那么p2就不执行
.finally(() => {}); //最后不管结果怎样都会执行一次
七、 Promise race
function step1() {
return new Promise((resolve, reject) => {
setTimeout(() => {
// console.log('step 1 finish');
resolve("111");
}, 2000);
});
}
function step2() {
return new Promise((resolve, reject) => {
setTimeout(() => {
// console.log('step 2 finish');
resolve("2222");
}, 4000);
});
}
// 同时执行多个异步操作,但是只取最快resolve的promise结果
Promise.race([step1(), step2()]).then((result) => {
console.log("race finish", result);
});
八、 Promise all
function step1() {
return new Promise((resolve, reject) => {
setTimeout(() => {
// console.log('step 1 finish');
resolve("111");
}, 2000);
});
}
function step2() {
return new Promise((resolve, reject) => {
setTimeout(() => {
// console.log('step 2 finish');
resolve("2222");
}, 4000);
});
}
//所有异步操作都成功后才执行(等最慢的那个),返回所有实例的结果
Promise.all([step1(), step2()]).then((result) => {
console.log("all finish", result);
});
1. promise all 的缺陷
例子:
Promise.all([A(), B(), C(), D()])
.then((res) => {
this.resA = res[0];
this.resB = res[1]; // 这个数据拿不到
this.resC = res[2];
this.resD = res[3];
})
.catch((err) => {
console.log(err);
});
promise.all
的用法: 传递一个 promise
的数组,当所有的 promise
都完成(resolved
),回调所有成功的结果 但是有一个失败, 回调第一个失败的结果,不执行then
回调 在当前状况下, hobbies 接口返回 500,promise 变成 rejected 状态,然后就不会执行 then 回调了。
2. 什么时候可以使用 Promise.all
既然一个 Promise 失败会影响所有 Promise 回调执行以及 promise.all 会有一些阻塞的情况,那么我们什么情况可以使用 Promise.all 呢? 例如: 几个异步操作是强相关的,后续操作必须依赖这几个返回结果才能进行。 举个栗子: 点餐查询用户的账号状态,用户的余额,会员信息,产品库存,商家状态等才能下单
Promise.all([A(), B(), C(), D()])
.then(() => {
// 下单和支付的操作
})
.catch((error) => console.log("异常订单", error));
3. 如何解决
用.then
。因为.then
返回的也是一个 promise
对象,不管是resolve
还是reject
都会执行.then
。返回promise
对象后就变成resovle
状态了。
function handlePromise(promiseList) {
return promiseList.map((promise) =>
promise.then(
(res) => ({ status: "success", res }),
(err) => ({ status: "fail", err })
)
);
}
Promise.all(
handlePromise([Promise.reject(1), Promise.resolve(2), Promise.resolve(3)])
).then(
(res) => console.log(res),
(err) => console.log(err)
);
// [
// {status: "fail", err: 1},
// {status: "success", res: 2},
// {status: "success", res: 3}
// ]