什么是promise
一个 Promise 对象代表一个目前还不可用,但是在未来的某个时间点可以被解析的值。Promise表示一个异步操作的最终结果。 从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。 promise为何存在?
举个例子:目前需要执行A和B两个函数,但是B函数只能在A函数完成以后才能开始,并不能同时开始。那么我们可能就会出现类似下面这个嵌套函数
function fn() {
$.ajax({
url: 'https://AAA.com',
type: 'post',
success: function (res) {
A (res) { // A函数里面执行完毕后开始执行B函数
// ....
B (res) {
// ...
}
}
}
})
}
目前虽然只有几行代码,但是当不断的重复上面的情况,代码就变得很可怕了,不断的回调嵌套,这也就是传说中的回调地狱。
那么为了解决这种情况,神圣的promise便 应运而生。它嘴里嘟囔着时代从不缺英雄,既然没人出手,那就由我promise来吧。那么时代的英雄是如何处理上面的事情呢?让我们来看看针对上面的情况promise最简单的写法
const heroPromise = new Promise((resolve, reject) => {
resolve()
})
heroPromiseHandle.then(res => {
A() // 处理A函数
}).then(res => {
B() // 处理B函数
})
事情好像变得有趣起来了,你可能会问A函数和B函数这样就能分开执行了吗?不着急,让我们从头开始,让我们先了解一下promise!
promide对象代表异步操作,分别有三种状态pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是Promise这个名字的由来,它的英语意思就是“承诺”, 表示其他手段无法改变。
基本用法
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 异步操作成功 */){
resolve(value); // 成功的回调
} else {
reject(error); // 失败的回调
}
});
其中promise会有两个回调函数,分别是resolve,reject。resolve表示promise从未完成状态变为成功(pending -> fulfilled)
reject表示promise从未完成状态变为失败(pending -> rejected),二者都是将最后的结果传递出去。看个例子
function heroPromise (num) {
return new Promise((resolve, reject) => {
if (num > 5) {
resolve(num)
} else {
reject(new Error('值小于5'))
})
}
var num4 = new heroPromise(4) // 传入num = 4
var num6 = new heroPromise(6) // 传入num = 6
num4.then(res => {
console.log(res)
}, err => {
console.log(err) // Error: 值小于5
})
num6.then(res => {
console.log(res) // 6
}, err => {
console.log(err)
})
好了,让我们看看分析一下上面的代码,首先是声明了一个构造函数heroPromise,返回一个promise对象,promise里面则是进行了参数num的判断,如果num>5那么执行成功的回调,反之则是抛出错误。这也就是为什么执行num4的时候只能捕获到error而第一步的打印res却并未执行,num6则相反。
可能你已经看到了,那个then是什么呀?我在函数中并没有声明这个方法,怎么就调用了呢?其实Promise 实例是具有then
方法滴,无需声明,直接调用即可。执行顺序的话就是先是执行promise内部的代码,然后执行完毕便是执行then方法了,然后便是链式调用一步一步的往下执行,只有当上一步的代码执行完毕才会跳转到下一个then,现在我们修改一下上面num6的then方法
num6.then(res => { console.log(res) // 6
for( let i = 0; i < res; i ++) {
console.log(1)
if(i = 5) {
i = 0
}
}
}).then(res => {
console.log('next')
})
我们在第一个then方法里写一个死循环,那么代码就永远都无法进行到下一步打印出next的值了。好了,现在我们已经解决了开头所说的AB函数执行的问题了,那么让我们来看看promise更详细的api
Promise.prototype.catch()
promise.prototype.catch()
等同于.then(null, rejection)
或.then(undefined, rejection)
的别名,用于指定发生错误时的回调函数。
num4.then(res => {
console.log(res)
}).catch(err => {
console.log(err) // Error: xxx
})
num4.then(res => {
console.log(res)
}, err => {
console.log(err) // Error: xxx
})
当catch没出现时,我们使用上面第二种方法捕获错误,现在有了catch只需要在then方法后面续上就可以了。
那么他是任何情况的错误都可以捕获吗,当然不是。在上面介绍的时候我们就已经说到
promide对象代表异步操作,分别有三种状态pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态
如果已经是在resolve回调执行完我们再抛出错误,它还能接收到吗?让我们来试试看
const heroPromise = new Promise(function(resolve, reject) {
resolve('success');
throw new Error('error:快来捕获我');
});
heroPromise.then(res=>{
console.log(res)
}).catch(err => {
console.log(err)
})
// success
很显然,后面那句的错误并不会被捕获到,所以在日常使用中我们要注意,切记将两种状态的回调区分开。当状态变成了已完成,就再都不会变为失败状态,就有些人一但错过,那么就永远不能够重来了。
Promise.prototype.finally()
promise.prototype.finally()
顾名思义finally即为最后的意思,finally()
方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。
什么意思呢?也就是说当异步操作结束以后不管状态是成功还是失败,都将执行finally方法,举个例子,当你进行搜索时,页面将进行loading,那么不管搜索结果是成功或者失败,loading效果都要消失,那么不使用finally方法,便是在then方法或者catch里面都加入去除loading效果的代码,因为你无法知道什么时候会返回什么状态。那么写在finally就再合适不过了,无论成功亦或失败都将移除loading。
使用上面的代码来试试 ,可以看到最后除了success还有有finally打印出来。
const heroPromise = new Promise(function(resolve, reject) {
resolve('success');
throw new Error('error:快来捕获我');
});
heroPromise.then(res=>{
console.log(res)
}).catch(err => {
console.log(err)
}).finally(() => {
console.log('finally')
})
// succes
// finally
Promise.all()
all方法返回一个新的promise对象,该promise对象在iterable参数对象里所有的promise对象都成功的时候才会触发成功,一旦有任何一个iterable里面的promise对象失败则立即触发该promise对象的失败。
const p1 = new Promise(function(resolve, reject) { resolve('p1'); });const p2 = new Promise(function(resolve, reject) { resolve('p2'); });const p3 = new Promise(function(resolve, reject) { resolve('p3'); });const p = Promise.all([p1, p2, p3])
p.then((res) => { console.log(res) // ['p1','p2','p3']}).catch(err => { console.log(err)})
上面代码中,Promise.all()方法接受一个数组作为参数,p1、p2、p3都是 Promise 实例,如果不是,就会先调用下面讲到的Promise.resolve方法,将参数转为 Promise 实例,再进一步处理。另外,Promise.all()方法的参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例。
p的状态由p1、p2、p3决定,分成两种情况。
- 只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
- 只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。
Promise.race()
rece()
用法跟all()
一样,都是将多个 Promise 实例,包装成一个新的 Promise 实例。
const p = Promise.race([p1, p2, p3])
不同的是只要p1
、p2
、p3
之中有一个实例率先改变状态,p
的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p
的回调函数。
还有几种方法,类型也是跟上面的all和race方法类似
- Promise.any()
- promise.allSettled()
介绍到这,那么可能心里会有点疑惑,这么写好烦啊,每次都要写这么一大串,有没有比较简便的写法,当然也有。让我们看看下面
Promise.resolve()
Promise.resolve()
等同于下面的写法。
Promise.resolve('foo')
// 等同于
new Promise(resolve => resolve('foo'))
- 返回一个状态由给定value决定的Promise对象。如果该值是一个Promise对象,则直接返回该对象;如果该值是thenable(即,带有then方法的对象),返回的Promise对象的最终状态由then方法执行决定;否则的话(该value为空,基本类型或者不带then方法的对象),返回的Promise对象状态为fulfilled,并且将该value传递给对应的then方法。通常而言,如果你不知道一个值是否是Promise对象,使用Promise.resolve(value) 来返回一个Promise对象,这样就能将该value以Promise对象形式使用。
const p = Promise.resolve('success')
p.then(res => {
console.log(res) // success
})
那么可能会问了,既然可以直接定义成功状态的回调,那能不能也定义失败的回调呢,当然也有,写法跟上面其实也是一样
Promise.reject()
const p = Promise.reject('error')
p.then(res => {
console.log(res)
}).catch(err => {
console.log(err) // error
})
到这里就算是结束了,其实promise也并不是全无缺点。在使用过程中无法得知执行到某一步,如果不添加回调函数并无法捕捉到错误。好了,纪录到这就算结束了。