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}
// ]