1、Promise
在Promise以前,在需要多个异步操作的时候,会导致多个回调函数嵌套,导致代码不够直观,就是常说的回调地狱。
aFunc(function(aRes) {
bFunc(aRes, function(bRes) {
cFunc(bRes, function(cRes) {
console.log('得到最终结果: ' + cRes);
}, callback);
}, callback);
}, callback);
通常通过 Promise 来解决,Promise本意是承诺,在程序中的意思就是承诺我过一段时间后会给你一个结果。 什么时候会用到过一段时间?答案是异步操作,异步是指可能比较长时间才有结果的才做,例如网络请求、读取本地文件等。
aFunc().then(function(aRes) {
return bFunc(aRes);
})
.then(function(bRes) {
return cFunc(bRes);
})
.then(function(cRes) {
console.log('得到最终结果: ' + cRes);
})
.catch(callback);
1.1、Promise状态
Promise 必须为以下三种状态之一:等待态(Pending)、执行态(Fulfilled)和拒绝态(Rejected)。一旦Promise 被 resolve 或 reject,不能再迁移至其他任何状态(即状态 immutable)。
1.2、Promise过程
- 初始化
Promise状态(pending) - 立即执行
Promise中传入的 fn 函数,将Promise内部 resolve、reject 函数作为参数传递给 fn ,按事件机制时机处理 - 执行 then(..) 注册回调处理数组(then 方法可被同一个 promise 调用多次)
-
Promise里的关键是要保证,then方法传入的参数 onFulfilled 和 onRejected,必须在then方法被调用的那一轮事件循环之后的新执行栈中执行。
2、Promise用法
Promise对象是一个构造函数,用来生成Promise实例。
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
Promise构造函数接受一个函数作为参数,该函数的两个参数分别是resolve和reject
-
resolve函数的作用是,将Promise对象的状态从“未完成”变为“成功” -
reject函数的作用是,将Promise对象的状态从“未完成”变为“失败”
3、Promise对象之实例方法
3.1、Promise.prototype.then()
Promise 实例具有 then 方法,也就是说,then方法是定义在原型对象Promise.prototype 上的。它的作用是为 Promise 实例添加状态改变时的回调函数。前面说过, then 方法的第一个参数是 resolved 状态对应的回调函数, 第二个参数(可选) 是 rejected 状态对应的回调函数。
3.2、Promise.prototype.catch()
Promise.prototype.catch() 方法是 .then(null,rejection) 或 .then(undefined,rejection) 的别名,用于指定发生错误时的回调函数。
getJSON('/posts.json').then(function(posts) {
// ...
}).catch(function(error) {
// 处理 getJSON 和 前一个回调函数运行时发生的错误
console.log('发生错误!', error);
});
上面代码中,getJSON()方法返回一个 Promise 对象,如果该对象状态变为resolved,则会调用then()方法指定的回调函数;如果异步操作抛出错误,状态就会变为rejected,就会调用catch()方法指定的回调函数,处理这个错误。另外,then()方法指定的回调函数,如果运行中抛出错误,也会被catch()方法捕获。
3.3、Promise.prototype.finally()
finally()方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。
getJSON('/posts.json').
.then(result => {···})
.catch(error => {···})
.finally(() => {···});
上面代码中,不管promise最后的状态,在执行完then或catch指定的回调函数以后,都会执行finally方法指定的回调函数。
3.4、Promise.all()
Promise.all() 方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
const p = Promise.all([p1, p2, p3]);
上面代码中,Promise.all()方法接受一个数组作为参数,p1、p2、p3都是 Promise 实例,如果不是,就会先调用下面讲到的Promise.resolve方法,将参数转为 Promise 实例,再进一步处理。另外,Promise.all()方法的参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例。
p的状态由p1、p2、p3决定,分成两种情况。
(1)只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
(2)只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。
3.5、Promise.race()
Promise.race()方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。
const p = Promise.all([p1, p2, p3]);
上面代码中,只要p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。
4、Promise使用场景
- 将图片的加载写成一个
Promise,一旦加载完成,Promise的状态就发生变化
const preloadImage = function (path) {
return new Promise(function (resolve, reject) {
const image = new Image();
image.onload = resolve;
image.onerror = reject;
image.src = path;
});
};
- 通过链式操作,将多个渲染数据分别给个
then,让其各自处理数据。或当下个异步请求依赖上个请求结果的时候,我们也能够通过链式操作友好解决问题
getData().then(res=>{
let { bannerList } = res
console.log(bannerList)
return res
}).then(res=>{
let { storeList } = res
console.log(storeList)
return res
}).then(res=>{
let { categoryList } = res
console.log(categoryList)
return res
})
- 通过
all()实现多个请求合并在一起,汇总所有请求结果
function initLoad(){
// loading.show()
Promise.all([getBannerList(),getStoreList(),getCategoryList()]).then(res=>{
console.log(res)
loading.hide()
}).catch(err=>{
console.log(err)
loading.hide()
})
}
//数据初始化
initLoad()
- 通过
race可以设置图片请求超时
function requestImg(){
var p = new Promise(function(resolve, reject){
var img = new Image();
img.onload = function(){
resolve(img);
}
//img.src = "xx.jpg"; 正确的
img.src = "xxx.jpggg"; // 错误的
});
return p;
}
//延时函数,用于给请求计时
function timeout(){
var p = new Promise(function(resolve, reject){
setTimeout(function(){
reject('图片请求超时');
}, 5000);
});
return p;
}
Promise
.race([requestImg(), timeout()])
.then(function(results){
console.log(results);
})
.catch(function(reason){
console.log(reason);
});
