来看一个async/await和Promise.then的使用对比
await紧跟一个Promise实例,返回一个resolve或reject的结果,属于同步任务
await后面行的内容相当于Promise.then()内执行的内容,就是异步任务
async
async声明的函数的返回本质上是一个Promise,所以要获得async声明的函数的返回值需要用then方法返回
async function test(){
return Promise.resolve("async")
// return "async"也可,因为会自动解析成Promise.resolve('async')
}
// test() 如果直接调用则返回Promise {<resolved>: "async"}对象
test().then(function(result){
console.log(result)
})
输出async
await
await是在等待一个Promise的异步返回,后面才能继续执行
一个延时输出的例子
async function test(ms){
console.log("hello")
await new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve()
},ms)
})
console.log("world")
}
test(1000)
输出hello
,1s后输出world
但是如果await跟的返回值不是Promise
async function test(ms){
console.log("hello")
await setTimeout(()=>{
console.log("延时")
},ms)
console.log("world")
}
test(1000)
输出hello world 延时,await没有起到异步的作用,setTimeout作为宏任务被放到最后执行。
也就是说一般await会跟Promise实例搭配实现异步,用来代替then
链式调用中的Promise和async await
设置间隔一段时间之后输出内容,sleep函数用于设置间隔时间,返回一个Promise函数
// 写一个函数返回Promise对象,
var sleep = function(ms){
return new Promise(function(resolve,reject){
// 判断输入类型,reject给后面的函数catch传入参数
if(typeof ms != "number"){reject(new Error("输入错误"))}
// 用计时器设置延时输出,resolve给后面的函数then传入参数
setTimeout(function(){
resolve("延迟了 " + ms + " 毫秒输出")
},ms)
})
}
用then的写法。then式链式写法的本质其实是一直往下传递返回一个新的Promise,也就是说then在下一步接收的是上一步返回的Promise
sleep(1000).then(function(result){
console.log(result)
return sleep(2000) // 链式调用的关键,上一层结束后返回一个Promise对象
}).then(function(result){ // 在下一层then执行上面Promise的resolve
console.log(result)
return sleep(5000)
}).then(function(result){
console.log(result)
}).catch(function(error){ // 捕获错误
console.log(error)
}).finally(function(){ // 无论是否catch到错误都会执行finally
console.log("这是一个延时输出函数")
})
用await的写法。await紧跟一个Promise对象,返回resolve或reject结果
(async ()=>{
try{
console.log(await sleep(1000))
console.log(await sleep(2000))
console.log(await sleep(3000))
}
catch(e){
console.log(e)
}
})()
用for循环+async/await实现链式调用
async function test(){
arr = [sleep(1000),sleep(2000),sleep(5000)]
for(var i = 0; i < arr.length; i++){
console.log(await arr[i])
}
}
test()
解决回调陷阱常用方法:
function拆解
事件发布/订阅模式
Promise
Generator
async / await
链式写法的错误处理
见上面用then的写法,只要在最后写一个catch即可,因为链式写法的错误处理具有“冒泡”特性,链式中任何一个环节出问题,都会被catch到,同时在某个环节后面的代码就不会执行了。
注意catch只是捕获错误的一个链式表达,并不是break!所以,catch放的位置也很有讲究,一般放在一些重要的、必须catch的程序的最后。这些重要的程序中间一旦出现错误,会马上跳过其他后续程序的操作直接执行到最近的catch代码块,但如果catch后面还有then还是会执行
return await
async function waitAndMaybeReject(){
// 等待1秒钟
await new Promise(resolve => setTimeout(resolve, 1000));
// 抛一枚硬币
const isHeads = Boolean(Math.round(Math.random()));
if(isHeads) return 'yay';
throw Error('Boo!');
}
async function foo() {
try {
return await waitAndMaybeReject();
}
catch (e) {
return 'caught';
}
}
拆开来看
async function foo() {
try {
// 等待 waitAndMaybeReject() 的结果来解决,
// 并且将 fullfill 的值赋给 fullfilledValue:
const fulfilledValue = await waitAndMaybeReject();
// 如果 waitAndMaybeReject() reject了,
// 我们的代码就会抛出异常,并且进入 catch 代码块的逻辑。
// 否则,这里的代码就会继续运行下面的语句:
return fulfilledValue;
}
catch (e) {
return 'caught';
}
}
在try/catch之外的代码块中执行return await是多余的(如前所述,直接return即可),甚至Eslint还专门有规则来检测这种场景,但是在try/catch代码块之内,Eslint就允许这种操作。