这是我参与更文挑战的第9天,活动详情查看:更文挑战
[重学JavaScript系列文章连载中...]
我们都知道JavaScript是属于“单线程”语言,一次只能完成一件任务,不能同时执行多个js代码任务。倘若存在耗时的任务,后续任务只能排队等待,这将拖延整个程序的执行,导致页面卡顿,甚至造成页面假死。为了解决这个问题,JavaScript语言引入了同步任务和异步任务,本文讲讲异步编程。
callback()回调函数
首先看看采用callback()回调函数处理异步。Ajax异步请求可能是最常见的异步编程了。看下例子:
$.ajax({
url:'/testurl',
success:function(){
// 回调处理请求成功
}
})
倘若存在多个嵌套请求,代码将陷入“回调地域”。导致的问题有:
- 代码臃肿,可读性差
- 代码耦合度高、可维护性差、难以复用
- 回调函数都是匿名函数,不方便复用
下面看看ES6的Promise,为异步编程提供了更合理的解决方案。
Promise生命周期
Promise对象存在三种状态,进行中(pending),已成功(fulfilled)和已失败(rejected)。Promise创建时处于pending状态,状态的改变只能两种可能。一种是成功的时候,将pending状态改变为fulfilled;另一种是失败时将状态改变为rejected。
Promise执行顺序
const promise = new Promise((resolve,reject)=>{
console.log(1)
resolve()
console.log(2)
})
promise.then(res=>{
console.log(3)
})
console.log(4)
promise.then(res=>{
console.log(5)
})
// 执行输出顺序为:1,2,4,3,5
- Promise创建后会立即执行,输出1。
- 执行resolve()函数,触发then()函数指定的回调函数,但是它要等当前线程中的同步代码执行完毕,因此依次输出2,再输出4
- 当所有同步代码执行完毕,执行then()函数,此时存在两个回调,依次执行输出3、5。
Promise处理异常
我们通常建议使用catch()去捕获Promise异常,这就使catch()函数和then()函数成对出现,catch()接收的就是执行rejected()函数传递的参数。
为啥建议采用catch()函数呢?看个例子
Promise.resolve()
.then(res=>{
throw new Error('error')
},err=>{
console.log('err',err)
}).catch(err=>{
console.log('err',err)
})
这个例子里then()函数的第二个函数不能捕获第一个函数抛出的异常,而catch()函数能捕获到第一个函数的异常,这就是建议采用catch()处理rejected状态的原因。
Promise.all()
const p = Promise.all([p1,p2,p3,p4])
p的状态由all()函数接收的所有Promise状态决定,只有所有接收的Promise实例状态都变为fulfilled,则p的状态才会变成fulfilled。其中存在一个rejected状态,则p状态为rejected。需要注意的是当接收的Promise实例已经定义了catch()函数,则其rejected状态会自行捕获消化,并不会触发Promise.all()函数的catch()函数。
const p1 = new Promise((resolve,reject)=>{
resolve(1)
})
const p2 = new Promise((resolve,reject)=>{
throw new Error('error')
}).catch(err=>err)
Promise.all([p1,p2]).then(res=>{
console.log(res) // [1,'error']
}).catch(err=>{
// 此处p2的异常并未被Promise.all捕获,而是自行消化。
// p2执行完catch()函数后,p2的状态实际为fulfilled。
console.log('异常',err)
})
Promise.race()
const p = Promise.race([p1,p2,p3])
如果接收的多个Promise实例中任何一个实例变化,那么p实例的状态就随之改变,最先改变的那个Promise实例返回值将作为实例p的回调函数入参。
可以实现这么个场景,Ajax请求3s未收到请求成功响应,则自动处理成请求失败。
const p1 = ajaxGet('testUrl')
const p2 = new Promise((resolve,reject)=>{
setTimeout(()=>{
reject(new Error('request timeout 3s'))
},3000)
})
const p = Promise.race([p1,p2])
Promise.resolve()
将传入的变量转换为Promise对象,等价于在Promise函数内调用resolve()函数。
Promise.resolve()
函数执行后,Promise
的状态会立即变为fulfilled
,然后进入then()
函数中处理。
Promise.resolve()
// 等价于
new Promise(resolve=>resolve())
Promise.reject()
函数一样,只不过进入的是catch()
函数。这里不再叙述。
至此,我们大体学习了异步编程之一的Promise。