异步代码的问题在于,不知道何时代码执行完毕。比如
let x = 3;
setTimeout(() => x = x + 4 , 1000)
由于下面的setTimeout是异步执行的,我们不能准确知道x值在什么时候改变了。
回调函数
ajax ({
url: 'url',
sucess :function () {
}
})
回调函数是一种解决方案。
回调函数在异步请求结束时,会回调一个函数,来通知异步请求的结束。
但是,回调函数有一个问题。如果需要多次进行异步请求,就会这样:
ajax ({
url: 'url1',
sucess :function () {
ajax ({
url: 'url2',
sucess :function () {
ajax ({
url: 'url3',
sucess :function () {
}
})
}
})
}
})
会变成嵌套的回调地狱,这样可读性差,复用性也差。
ES6对于处理异步代码提出了Promise的解决方式。
Promise
Promise是一种异步代码解决机制。
Promise是一种状态机
Promise有三种状态:pending(进行中)、fulfilled(已成功)、rejected(已失败)
这三种状态抽象地表示了这段异步代码的状态。
- pending表示尚未开始,或者正在执行中
- fulfilled表示已经成功
- rejected表示没有成功
创建Promise时,Promise处于pending状态。状态的改变有两种情况:
- Promise执行成功,由pending状态变为fulfilled状态
- Promise执行失败,由pending状态变为rejected状态
见上图,状态机只有一个灯,所以只能处于一种状态。并且状态一旦改变,就不能再改变了。
创建promise实例时,需要传入一个执行器函数,这个执行器函数接收两个函数作为参数,并且这个执行器函数会立即执行。
const promise = new Promise((resolve, reject) => {
// 异步请求处理
if(/异步请求标识符/) {
resolve()
} else {
reject()
}
})
并且,执行器函数是同步执行的。promise.then()才是异步执行的。
Promise函数的结构
const promise = new Promise((resolve, reject) => {
// 异步请求处理
if(/异步请求标识符/) {
resolve()
} else {
reject()
}
})
Promise的值传递
如果我们只需要判断之后,得知异步请求是成功还是失败,那么我们只需要执行上述的“状态机”就可以了。比如网络请求,如果状态码在200-299之间,异步请求成功,状态机状态变为完成;如果状态码不在200-299之间,异步请求失败,状态机状态变为失败。
但有时,我们在异步处理完成后是需要传值的,只告知状态变化是不够的。
Promise的基本流程
在执行器函数中,判断异步请求的结果。如果结果为true,异步请求成功,则调用resolve()函数;如果结果为false,异步请求失败,则调用rejected()函数。
对于成功或者是失败的异步请求进行处理,就需要用到then()函数和catch()函数。
resolve()是then()的数据源,then中接收的参数,是由resolve传递进来的,这个由resolve()传递给then()的参数叫做解决值。
只要promise中出现了异常,就会自动调用reject,改变状态,这个由reject传递给catch()的参数叫做拒绝理由。出现异常会自动被catch捕获,不需要我们去特意调用一次reject。
Promise传值过程
resolve()传递的值,是then()的参数
const promise = new Promise((resolve, reject) => {
resolve(1)
})
promise.then((result) => {
console.log(result) //1
})
上一个then()中return的值,是下一个then()的参数
const promise = new Promise((resolve, reject) => {
resolve(1)
})
promise.then((result) => {
console.log(result) //1
return 2
}).then((result) => {
console.log(result) //2
})
链式调用
调用then函数时,回调函数中会接受一个函数,并且调用后,会返回一个新的Promise的实例,因此可以链式调用。上一轮then()调用的return值,会当做下一轮then()函数接收的参数值。
catch()是调用了rejected(),即执行失败后的回调函数。
Promise.all()和Promise.race()
Promise.all()
all:意为“全部”。它的状态由所有promise决定。
const p = Promise.all([p1, p2, p3])
所有的promise都fulfilled才会fulfilled,有一个失败了,第一个失败reject的返回值就会作为catch()的回调函数的参数。
Promise.race()
race:意为“竞争”。主要看最先是谁变化,它的状态会跟随最先变化的走。
const p = Promise.race([p1, p2, p3])
race()其中任意一个promise,一旦成功或者失败,Promise.all()的状态就按照它来走。