一隻sync和一隻wait

最近有一個包裏用了不少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 {
    // ...
  }
})()
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章