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);
});