iT邦幫忙

2021 iThome 鐵人賽

DAY 25
0
自我挑戰組

JavaScript 奇奇怪怪的核心觀念系列 第 25

(Day25) Promise 語法糖 async/await

前言

async/awaitPromise 的語法糖,最主要是提升了 Promise 在鏈式寫法的可讀性,而 async/await 能辦到的 Promise 本身都能辦到,所以不少教學都提到要先明白 Promise ,才會看懂 async/await

async/await 介紹:

  • async 直接翻譯成中文是異步,也就是非同步的意思,若要使用 async 會將 async 放在函式旁,代表這個函式接下來會以同步方式來執行非同步的語法。

  • await 翻譯成中文,則是等待, 一般是放在 async 函式中的 Promise 事件旁,代表接下來的程式碼會等待 await 方法完成後才執行。

async/await 他們算是一組的,基本上使用碰上使用時機時,這兩個方法都會同時被使用 ,這部分用文字說明可能不是這麼好懂,直接來使用 Promise 章節提到的 Promise 鏈式寫法,以及 async/await 來做對比會比較好理解:

  • Promise 鏈式寫法:
const p = new Promise((resolve) => {
  if (true) {
    resolve(`直接執行 Promise`)
  }
})

function PromiseFn(String) {
  return new Promise((resolve, reject) => {
    setTimeout(function () {
      if (String.length >= 5) {
        resolve(`Promise 成功, ${String} 共 ${String.length} 個字 `)
      } else {
        reject('Promise 失敗')
      }
    }, 2000);
  });
}

PromiseFn('Ryder')
  .then((res) => {
    console.log(res) // Promise 成功, Ryder 共 5 個字
    return PromiseFn('youtube')
  }).then((res) => {
    console.log(res)  // Promise 成功, youtube 共 7 個字 
    return p
  }).then((res) => {
    console.log(res) // 直接執行 Promise
  })
  • async/await 則可寫成:
const p = new Promise((resolve) => {
  if (true) {
    resolve(`直接執行 Promise`)
  }
})

function PromiseFn(String) {
  return new Promise((resolve, reject) => {
    setTimeout(function () {
      if (String.length >= 5) {
        resolve(`Promise 成功, ${String} 共 ${String.length} 個字 `)
      } else {
        reject('Promise 失敗')
      }
    }, 2000);
  });
}

async function usePromise() {
  const data1 = await PromiseFn('Ryder');
  console.log(data1) // Promise 成功, Ryder 共 5 個字
  const data2 = await PromiseFn('youtube');
  console.log(data2) // Promise 成功, youtube 共 7 個字
  const data3 = await p
  console.log(data3); // 直接執行 Promise
}
usePromise();

扣掉 console.log 可以發現 async/await 的寫法省去大量的 then() ,因此讓程式碼變的較容易閱讀。
await 其實可以塞入其他表達式的,但是實做中通常不會這麼使用:

const p = new Promise((resolve) => {
  if (true) {
    resolve(`直接執行 Promise`)
  }
})

function PromiseFn(String) {
  return new Promise((resolve, reject) => {
    setTimeout(function () {
      if (String.length >= 5) {
        resolve(`Promise 成功, ${String} 共 ${String.length} 個字 `)
      } else {
        reject('Promise 失敗')
      }
    }, 2000);
  });
}

async function usePromise() {
  const data1 = await 1+1;
  console.log(data1) // 2
  const data2 = await PromiseFn('youtube');
  console.log(data2) // Promise 成功, youtube 共 7 個字
  const data3 = await p
  console.log(data3); // 直接執行 Promise
}
usePromise();

try...catch

使用 async/await 的寫法 ,只要使用 awaitPromise 只要一失敗,接下來會回傳 Promisereject 資料,接著 await 下的程式碼都不會被執行,比如:

const p = new Promise((resolve) => {
  if (true) {
    resolve(`直接執行 Promise`)
  }
})

function PromiseFn(String) {
  return new Promise((resolve, reject) => {
    setTimeout(function () {
      if (String.length >= 5) {
        resolve(`Promise 成功, ${String} 共 ${String.length} 個字 `)
      } else {
        reject('Promise 失敗')
      }
    }, 2000);
  });
}

async function usePromise() {
  const data1 = await PromiseFn('Ryder');
  console.log(data1) // Promise 成功, Ryder 共 5 個字
  const data2 = await PromiseFn('test');
  console.log(data2) // Promise 失敗
  const data3 = await p
  console.log(data3); 
}
usePromise();

這種狀況會容易造成不清楚錯誤來源,在維護上會較為困難,也因此 async/await 的寫法,有提供另一種將 Promise 執行成功、失敗分開的寫法,就是 try…catchtry 專門存放執行 Promise 成功的結果,而 catch 則是執行 Promise 失敗的結果,這個方法跟原生 Promise.then().catch() 十分相似,如範例:

const p = new Promise((resolve) => {
  if (true) {
    resolve(`直接執行 Promise`)
  }
})

function PromiseFn(String) {
  return new Promise((resolve, reject) => {
    setTimeout(function () {
      if (String.length >= 5) {
        resolve(`Promise 成功, ${String} 共 ${String.length} 個字 `)
      } else {
        reject('Promise 失敗')
      }
    }, 2000);
  });
}

async function usePromise() {
try{
  const data1 = await PromiseFn('Ryder');
  console.log(data1) //Promise 成功, Ryder 共 5 個字 
  const data2 = await PromiseFn('test'); // Promise 執行 catch
  console.log(data2) 
  const data3 = await p
  console.log(data3); 
	}
	catch (err) {
    console.log('catch', err); //catch Promise 失敗
  }
}
usePromise();

要注意的是 try 中的 Promise 執行失敗,是會執行 catch 中的程式碼,而 try 中的程式碼一樣不會繼續執行下去。

async/await 混搭 All 與 Race

一開始有提到 async/awaitPromise 的語法糖,因此 async/await 其實以可以和 Promise.allPromise.race 一同使用,這也算是目前比較常用再處理 Ajax 處理順序的寫法:


const p = new Promise((resolve) => {
  if (true) {
    resolve(`直接執行 Promise`)
  }
})

function PromiseFn(String) {
  return new Promise((resolve, reject) => {
    setTimeout(function () {
      if (String.length >= 5) {
        resolve(`Promise 成功, ${String} 共 ${String.length} 個字 `)
      } else {
        reject('Promise 失敗')
      }
    }, 2000);
  });
}

async function usePromise() {
	const data1 = await Promise.all([PromiseFn('Ryder'), PromiseFn('youtube')])
	console.log(data1) // ['Promise 成功, Ryder 共 5 個字 ', 'Promise 成功, youtube 共 7 個字 ']
	const data2 = await p
	console.log(data2) // '直接執行 Promise'
}

參考文獻


上一篇
(Day24) 處理非同步的 Promise
下一篇
(Day26) 使用 fetch 串接 Ajax
系列文
JavaScript 奇奇怪怪的核心觀念30

尚未有邦友留言

立即登入留言