文章目录
前言
async…await的使用以及事件循环机制(同步,宏任务,微任务)的执行顺序
提示:以下是本篇文章正文内容,下面案例可供参考
一、async…await是什么?
在 JavaScript 中,使用 async
和 await
关键字可以简化异步操作的处理。async
用于定义一个返回 Promise 的异步函数,而 await
用于等待一个 Promise 对象的解析结果。
二、使用过程
1.async
函数返回的是一个promise
对象
代码如下(示例),下面两种方法是等效的:
// 方法1
function f() {return Promise.resolve('TEST');
}// asyncF is equivalent to f!
// 方法2
async function asyncF() {return 'TEST';
}
async function timeout() {
return 'hello world!'
}
timeout().then(val => {
console.log(val)
})
console.log('我虽然在后面,但是先执行')
}
那么要想获取到async 函数的执行结果,就要调用promise的then 或 catch 来给它注册回调函数
原来async 函数返回的是一个promise 对象,并且Promise还有state和result,如果async函数中有返回值,当调用该函数时,内部会调用Promise.resolve()方法把它转化成一个promise对象作为返回,但如果timeout函数内部抛出错误呢? 那么就会调用Promise.reject() 返回一个promise 对象
2.await 等到了要等的,然后呢?
await 等到了它要等的东西,一个 Promise 对象,或者其它值,然后呢?
1)如果它等到的不是一个Promise对象,那么await表达式的运算结果就是它等到的东西。
2)如果它等到的是一个Promise对象,await就忙起来了,它会阻塞函数后面的代码,等着Promise对象resolve,然后得到resolve的值,作为await表达式的运算结果。
await 优势在于处理 then 链,使代码看起来像同步代码一样
3.await处理
当遇到 await
时,会阻塞函数内部处于它后面的代码(而非整段代码),去执行该函数外部的同步代码;当外部的同步代码执行完毕,再回到该函数执行剩余的代码。
async function fn1 (){
console.log(1)
await fn2() // fn2进入微任务队列等待执行
console.log(2) // 阻塞
}
async function fn2 (){
console.log('fn2')
}
fn1()
console.log(3)
// 输出结果:1,fn2,3,2
上面的例子中,await
会阻塞它下面的代码(即加入微任务队列
),先执行 async 外面的同步代码,同步代码执行完,再回到 async 函数中,再执行之前阻塞的代码。
注意
1)await
必须写在async
函数中, 但async函数中可以没有await
2)如果await的promise失败了, 就会抛出异常, 需要通过**try…catch**
来捕获处理
总结
async/await or promise使用时到底选择哪个呢?
大部分复杂情况下async/await 的确是最优解,个人觉得也不是所有情况下都是 async/await 写起来更爽,最不爽的就是他的错误处理,try…catch这个代码看起来就很奇怪(当然也有很多人喜欢这种错误处理方式)。所以我个人的习惯,当只有一个异步请求,且需要做错误处理的情况下,更倾向于使用 promise。比如
// promise
getInfo()
.then(res => {
//do somethings
})
.catch(err => {
//do somethings
})
// async/await
try {
const res = await getInfo()
//do somethings
} catch (error) {
//do somethings
}
async…await和 .then (promise) 可以不必同时出现
三、JS的事件循环机制
(Event Loop)-- 同步 . 宏任务 . 微任务执行顺序:
实例一:
console.log('同步代码1');
async function async1 () {
console.log('async1 start')
await async2()
console.log('async1 end')
}
async function async2 () {
console.log('async2')
}
async1()
setTimeout(() => {
console.log('setTimeout1')
new Promise((resolve) => {
console.log('Promise 1')
resolve()
}).then(() => {
console.log('promise.then1')
})
}, 10)
new Promise((resolve) => {
console.log('同步代码2, Promise 2')
setTimeout(() => {
console.log('setTimeout2')
}, 10)
resolve()
}).then(() => {
console.log('promise.then2');
setTimeout(() => {
console.log('setTimeout3')
}, 10);
new Promise((resolve) => {
console.log('Promise 3')
resolve()
}).then(() => {
console.log('promise.then3')
});
})
console.log('同步代码3');
/* 宏任务队列中第1个宏任务script的打印:*/
// 同步代码1
// async1 start
// async2
// 同步代码2, Promise 2
// 同步代码3
// async1 end
// promise.then2
// Promise 3
// promise.then3
/* 宏任务队列中第2个宏任务setTimeout1的打印:*/
// setTimeout1
// Promise 1
// promise.then1
/* 宏任务队列中第3个宏任务setTimeout2的打印:*/
// setTimeout2
/* 宏任务队列中第3个宏任务setTimeout3的打印:*/
// setTimeout3
注意:
1、promise中的回调函数立刻执行,then中的 回调函数 会推入微任务队列中,等待执行栈所有任务执行完才执行。
2、通过 async 定义的函数在 执行栈 中执行,await 将异步程序变成同步,所以await 后面执行的程序需要等到await定义的函数执行完毕才执行,需要放入微任务队列中等待的。(await在等待后面的函数执行的过程中会先执行下面的同步任务,执行完之后再执行await后面的代码)
实例二:
queueMicrotask(() => {
console.log("queueMicrotask1")
});
queueMicrotask表示开启一个微任务,与Promise.then函数效果一致
实例三:
async function async1 () {
console.log('async1 start')
await async2();
console.log('async1 end')
}
async function async2 () {
console.log('async2')
}
console.log('script start')
setTimeout(function () {
console.log('setTimeout')
}, 0)
async1();
new Promise (function (resolve) {
console.log('promise1')
resolve();
}).then (function () {
console.log('promise2')
})
console.log('script end')
结果
script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout
函数只有调用的时候才会推入调用栈中,所以最先执行的是 console.log,即输出 script start,然后setTimeout函数(“setTimeout”)放入宏任务队列中等待,调用async1函数,输出 async1 start,执行async2函数,输出async2,(“async1 end”)放入微任务队列中等待,继续向下执行Promise函数,输出 promise1,then函数中的(“promise2”)放入微任务队列中等待,输出 script end。
调用栈的程序都已执行完毕,此时开始执行微任务队列中的程序,依次输出 async1 end、promise2。
结论:通过async定义的函数在调用栈中执行,await 将异步程序变成同步,所以await后面执行的程序需要等到await定义的函数执行完成才执行,需要在微任务队列中等待