最近有一個包裏用了不少async await,但是細細看了一下發現很多地方用的比較奇怪,比如同步的函數,沒有用到await也用async,就稍微整理了一下相關的使用,沒有實現原理相關的內容,以示例爲主,有點像面試題。參考了一些文章,就不單獨羅列了,一搜還挺多的。
async返回了什麼
async返回一個promise,這個大家應該都很清楚,那麼看一下這個例子,看看是否能區分
async function sleep(second) {
// 把這個return去掉和不去掉,輸出有什麼區別
return await new Promise(resolve => {
resolve("sleeped");
});
}
console.log(sleep().then(console.log));
上面這個例子,sleep函數寫了return的時候,返回的是await後面的promise,打印出的是sleeped
,去掉return,打印出的是undefined
,可以認爲,這時候async返回的是一個隱藏的Promise.resolve(),我們可以用以下例子來驗證
async function num(){
return 33;
}
console.log(num());
console.log(Promise.resolve(33));
循環中的async
先來看看for中使用async的姿勢
(async function() {
for (let i = 0; i < 3; i++) {
// 這個例子其實是考察Promise的
await new Promise(res => {
res();
console.log(i);
});
}
console.log(123);
})();
(async function() {
for (let i = 0; i < 3; i++) {
await new Promise(res => {
res(i);
// setTimeout(()=>res(i), 3000);
}).then(console.log);
}
console.log(123);
})();
再來看看map中使用async的姿勢,使用map有兩個地方可以放await,一個是map前面,一個是map內的函數,
(async function() {
await [1,2,3].map(i=>{
// 如果這個例子的結果和你想的不一樣,可以想想map返回了啥
// 注意這個return
return new Promise(res => {
res(i);
// 如果寫成這樣,結果會不會不一樣
// setTimeout(()=>res(i), 3000);
}).then(console.log);
})
console.log(123);
})();
(async function() {
[1,2,3].map(async i=>{
// 這裏最外層的那個async沒啥用
await new Promise(res => {
res(i);
// 如果寫成這樣,結果會不會不一樣
// setTimeout(()=>res(i), 3000);
}).then(console.log);
})
console.log(123);
})();
第一個例子,map返回的其實是一個數組,裏面有三個Promise,所以如果想要等待map中的Promise執行完畢,需要在map外面包一個Promise.all。
第二個例子,先輸出了123,應該不是一般需要的那種結果。
如果仔細的話,會發現使用定時3秒輸出的時候,map的兩個例子是3個一起輸出的,而for循環的3個輸出是一個接一個。
其他的循環類型,可以自己試一試
到底要不要wait
async function sleep(second) {
await new Promise(res=>{
console.log(789)
setTimeout(()=>res(123), 3000);
}).then(console.log);
}
(async function(){
// 這個await去掉和不去掉的區別
await sleep();
console.log(456);
})()
我開始看到這個例子的時候,覺得調用sleep的時候可以不需要await,因爲sleep自己帶有await,等待Promise執行完畢,但是一試,發現和我想的不一樣,如果打印出sleep的返回,就比較清楚了,sleep()返回了一個Promise(這個我們在開頭就說了),所以如果不帶上await,後面的console是不會等待sleep的,所以如果需要同步調用一個async函數,一定要await,不管它裏面寫成啥樣(如果它內部直接返回常數,可以試試上面那個返回33的例子)
並行的async
在一些情況下我們是需要並行多個Promise的,比如多個請求一起發送,那麼就不能一個一個await下來,比較常用的Promise.all肯定沒問題,上面提到的map也可以,還發現了一種,別人寫的,但是忘了記錄是哪篇文章,看一下例子
async function fetchUserParallel () {
const namePromise = new Promise(res=>setTimeout(_=>res('name'),3000))
const avatarPromise = new Promise(res=>setTimeout(_=>res('avatar'),3000))
// 這種形式挺有意思的
return {
name: await namePromise,
avatar: await avatarPromise
}
}
(async function () {
console.time('should be 3s : ')
const user2 = await fetchUserParallel()
console.log(user2)
console.timeEnd('should be 3s : ')
})()
錯誤處理
最簡單和常用的就是try catch,catch有兩種寫法(可能更多)
try{
await promise();
}catch(){
...
}
await promise().catch(){
...
};
還有一種,也是看來的,很有意思
function transform(promise) {
return promise
.then(data => {
// 其實也可以只返回一個值,通過判斷返回值的類型來區分是否出錯了
return [null, data];
})
.catch(err => [err]);
}
(async function asyncTask() {
let err, data;
[err, data] = await transform(promiseRequest);
if (err) {
// ...
} else {
// ...
}
})()