开始的异步处理方式
getDataA(params, function(dataA){
// ...
getDataB(params, function(dataB){
// ...
getDatac(params, function(dataC){
// ...
})
})
})
开始的异步处理方式是这种通过callback进行处理,一步步去处理,当需要处理N步时就要有N个回调函数,代码就会纵向发展,同时使阅读性大大降低。
这种方式有个名字叫:地狱回调!很好理解,就像地狱一样,一层一层的进行调用。
Promise
基本用法
// 创建一个Promise对象
const promise = new Promise(function(resolve, reject) {
// 你可以在里面执行你的异步操作,例如请求数据
getDataA(params, function(dataA) {
if(dataA) {
// 改变状态为成功并传入数据
resolve(dataA)
}
// 改变状态为失败并传入错误信息
reject(new Error(dataA))
})
})
promise.then(function(dataA) {
// 成功后的回调
}, function(err) {
// 失败后的回调
})
Promise对象接收一个函数作为参数,这个函数拥有两个参数,代表成功(resolve)和代表失败(reject)的函数
这里暴躁老哥就要说了:这不还是要回调,只不过包了一层,完全是多此一举!
大佬您别急,请继续往下看~~
Promise执行
console.log('Hello')
const promise = new Promise(function(resolve, reject) {
console.log('Hi Promise')
setTimeout(function() {
console.log(1)
resolve('成功了')
}, 1000)
setTimeout(function() {
console.log(2)
reject('失败了')
}, 2000)
})
promise.then(function(res) {
console.log(res);
}, function(err) {
console.log(err);
})
console.log('Bay')
// Hello
// Hi Promise
// Bay
// 1
// 成功了
// 2
可以看到promise在创建时就会调用,并且会告诉异步的操作:哎,你先处理着,完了告诉我结果,我先去忙别的了。
Promise的特点:
1.Promise创建时就会执行
2.Promise会有三个状态,处理中、成功、失败
3.即使出了结果,里面的异步操作并不会停止!
4.虽然出了结果依旧会执行完所有异步,但是以第一个结果为准
暴躁老哥一拍桌子!好家伙,拿到结果就跑,后面说啥不管了。
Promise.prototype.then
let num = 0;
function nextF(data) {
num +=1;
console.log(num, data)
return data;
}
const promise = new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('成功了')
}, 1000)
})
promise
.then(nextF)
.then(nextF)
.then(nextF)
// 1 成功了
// 2 成功了
// 3 成功了
这里我定义了一个函数nextF,和一个变量num。nextF函数每次执行会使num+1并打印后把参数return。然后我把它传给promise.then,通过打印出的结果可以看出,then返回的是一个新的promise,并且默认是Fulfilled状态,所以then可以链式调用。
Promise.prototype.catch
let num = 0;
function nextF(data) {
num +=1;
console.log(num, data)
return data;
}
const promise = new Promise(function(resolve, reject) {
setTimeout(function() {
reject('失败了')
}, 1000)
})
promise
.catch(nextF)
.catch(nextF)
.catch(nextF)
// 1 失败了
这里改写了一下上面的例子,把promise的状态改为Rejected,发现catch方法只执行了一次,同样连续调用不会报错。所以catch方法和then方法一样会返回一个新的promise,并且间接证明了then和catch返回的新promise都是默认成功状态。
同时catch方法还有一个作用,捕获promise抛出的错误。
暴躁老哥:你这也证明不了返回的新promise默认是Fulfilled状态呀!那您接着往下看
let num = 0;
function nextF(data) {
num +=1;
console.log(num, data)
return data;
}
const promise = new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('成功了')
}, 1000)
})
promise
.then(nextF)
.then(function(data) {
num +=1;
console.log(num, data)
throw new Error('我喜欢红色')
})
.then(nextF)
.then(nextF)
.catch(nextF)
.then(nextF)
// 1 成功了
// 2 成功了
// 3 Error: 我喜欢红色
// 4 Error: 我喜欢红色
这里在第二个then方法中抛出一个错误,结果打印了两个成功了、两个失败了。为啥两个失败了,因为catch捕获了错误并返回,而返回的是一个新的promise,而这个新的promise默认Fulfilled状态,导致最后的then方法运行了,打印出了第二个失败了。
这里也证明了一个很神奇的功能,那就是当promise抛出错误时,会跳过后面的then直到被catch捕获。promise出现Rejected时也一样。
所以reject的作用是抛出一个异常!暴躁老哥先坐下,我知道你要说什么。当promise是Rejected状态时,并且后面没有进行捕获时,控制台会打印出一个Error!
Promise.prototype.finally
new Promise(function(resolve, reject) {
resolve('成功了')
})
.then(function(res) {
console.log(res)
return res
})
.catch(function(err) {
console.log(err);
return err
})
.finally(function(data) {
console.log('finally', data);
return data
})
.then(function(res) {
console.log(res)
return res
})
// 成功了
// finally undefined
// 成功了
// 另一个例子
new Promise(function(resolve, reject) {
throw new Error('红扑扑')
})
.then(function(res) {
console.log(res)
return res
})
.catch(function(err) {
console.log(err);
return err
})
.finally(function(data) {
console.log('finally', data);
return data
})
.then(function(res) {
console.log(res)
return res
})
// Error: 红扑扑
// finally undefined
// Error: 红扑扑
finally和then、catch方法一样返回一个新的promise并且默认Fulfilled状态。但是他接收的回调函数没有任何参数,同样返回值不会影响后面的行为,并且无论什么状态都会执行。所以一般只会用来在处理promise完毕后的善后工作
Promise.resolve
const p = Promise.resolve('a')
p.then(function(data) {
console.log(data);
})
// a
// 打印p会得到一个Promise实例
// 所以
Promise.resolve('a')
// 等价于
new Promise(function(resolve) {
resolve('a')
})
1.参数是一个 Promise 实例
const p = Promise.resolve(
new Promise(function(resolve, reject) {
setTimeout(function() {
resolve('失败了')
})
})
)
p.then(function(data) {
console.log(data);
})
.catch(function(err) {
console.log(err)
})
// 失败了
会执行这个Promise,所以原本Promise.resolve代表Fulfilled本应会执行then,但是并未走then而是直接执行了catch
2.参数是一个thenable对象
什么是thenable对象?
thenable对象就是拥有then方法对象
let thenable = {
a: 1,
then: function(resolve, reject) {
console.log(this.a)
reject(42);
}
};
let p1 = Promise.resolve(thenable);
p1.then(function (value) {
console.log('then', value)
})
.catch(function(err) {
console.log('catch', err)
});
// 1
// catch 42
会把这个thenable对象转换成Promise对象并执行then方法,所以跳过了p1.then执行了p1.catch。
3.不传参数
let p1 = Promise.resolve();
p1.then(function (value) {
console.log('then', value)
})
.catch(function(err) {
console.log('catch', err)
});
// then undefined
Promise.resolve允许不传参数,但是会直接返回一个Fulfilled状态的Promise
Promise.reject
Promise.reject的用法等同于Promise.resolve
let p1 = Promise.reject();
p1.then(function (value) {
console.log('then', value)
})
.catch(function(err) {
console.log('catch', err)
});
// catch undefined
区别是resolve传入的如果是一个promise对象或者thenable对象会执行,以得到的结果为准,如果不是则转换为Promise对象并执行resolve,参数就是传入的参数。而reject无论传入什么,都会返回一个rejected状态的Promise,而传入的参数就是失败或者说错误的理由
批量处理Promise对象
const p1 = new Promise(function(resolve, reject){
setTimeout(function() {
resolve('p1')
}, 1000)
})
const p2 = new Promise(function(resolve, reject){
setTimeout(function() {
resolve('p2')
}, 2000)
})
const p3 = new Promise(function(resolve, reject){
setTimeout(function() {
reject('p3')
}, 500)
})
const p4 = new Promise(function(resolve, reject){
setTimeout(function() {
resolve('p4')
}, 0)
})
这里创建p1、p2、p3、p4四个Promise实例
Promise.all
Promise.all([p1, p2, p4])
.then(res => {
console.log('success', res)
})
.catch(err => {
console.log('error', err)
})
// success ["p1", "p2", "p4"]
Promise.all([p1, p3, p4])
.then(res => {
console.log('success', res)
})
.catch(err => {
console.log('error', err)
})
// error p3
Promise.all接受一个数组作为参数,数组的项如果是Promise对象就会直接执行,如果不是就会默认执行上面说到的Promise.resolve进行转换,并返回一个新的Promise对象,新的Promise的状态取决于传入的一系列Promise对象:
1.p1、p2、p3的状态都为Fulfilled,这个Promise对象的状态就是Fulfilled,传入then方法是一个数组对应p1、p2、p3
2.p1、p2、p3在执行中有一个出现Rejected,就会直接调用catch,错误信息即最先Rejected的信息
Promise.race
Promise.race([p1, p2, p4])
.then(res => {
console.log('success', res)
})
.catch(err => {
console.log('error', err)
})
// success p4
Promise.race([p1, p3])
.then(res => {
console.log('success', res)
})
.catch(err => {
console.log('error', err)
})
// error p3
Promise.race的用法等同于Promise.all,区别于Promise.race的状态是赛跑原则。取决于得到的第一个状态
Promise.allSettled
Promise.allSettled([p1, p2, p3, p4])
.then(res => {
console.log('success', res)
})
.catch(err => {
console.log('error', err)
})
/* success [
{
status: "fulfilled",
value: "p1"
},
{
status: "fulfilled",
value: "p2"
},
{
status: "rejected",
value: "p3"
},
{
status: "fulfilled",
value: "p4"
}
]
*/
Promise.allSettled的用法等同于Promise.all,区别于Promise.allSettled不会出现rejected状态,它会等所有的Promise执行完之后一起返回
Promise.any
Promise.any([p1, p2, p3, p4])
.then(res => {
console.log('success', res)
})
.catch(err => {
console.log('error', err)
})
// success p4
Promise.any的用法和Promise.race一致,区别于正好相反,Promise.any是当参数实例全部rejected时才会是rejected,当参数实例中出现一个Fulfilled它的状态就会是fulfilled,传入then的参数就是这个参数实例的结果
Promise处理异步
最初的例子用Promise改写
new Promise(function(resolve, reject) {
getDataA(params, function(data) {
if(data){
resolve(data)
}else {
reject(new Error(data))
}
})
})
.then(function(dataA) {
return new Promise(function(resolve, reject) {
getDataB(params, function(data) {
if(data){
resolve(data)
}else {
reject(new Error(data))
}
})
})
})
.then(function(dataB) {
return new Promise(function(resolve, reject) {
getDataC(params, function(data) {
if(data){
resolve(data)
}else {
reject(new Error(data))
}
})
})
})
// ...
.catch(function(err) {
// ...
})
结论
1.Promise虽然未解决地狱回调,但是解决了代码纵向发展的趋势,使可读性更高
2.虽然解决了纵向发展的问题,但是在上面的例子请求数据时,最后的请求无法直接使用第一次请求的结果
参考