iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 29
1
自我挑戰組

JavaScript 試煉之旅系列 第 29

JavaScript Async 與 Await

今天要學習的是 AsyncAwait,是為了簡化 Promise 物件的操作流程而誕生。

來看看 MDN 怎麼描述:

async/await 函式的目的在於簡化同步操作 promise 的表現,以及對多個 Promise 物件執行某些操作。就像 Promise 類似於具結構性的回呼函式,同樣地,async/await 好比將 generator 與 promise 組合起來。

原本使用 Promise 物件時,我們會需要透過 then()catch 取得 resolvereject 的結果。 但 async/await 的出現簡化了透過then()catch 取得值的過程

來看一個測試例子:

//定義一組 promise
const runTime = (time, person) => {
  return new Promise((reslove, reject) => {
    setTimeout(() => {
      reslove(`${person}`)
    }, time);
  });
}
// 透過 then() 取得 resolve 的結果
const result = runTime(3000, 'Bill').then(res => console.log(res));
console.log(result);

上述預期可以得到的值為 Bill,應該沒有問題。

再來看看透過 async/await 改寫一下上面的例子:

function runTime(time, person) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(`${person}`);
    }, time);
  });
}

async function getPerson() {
    const result = await runTime(3000, 'Bill');
    console.log(result);
  }
getPerson();

在前面有提到,async/await 的出現簡化了透過then()catch 取得值的過程

所以預期可以得到的值為 Bill

先記住這個差別,接下來要提提 async/await 為什麼能夠這麼做。

以下是 MDN的語法:

async function name([param[, param[, ... param]]]) {
   statements
}
  • name
    • 函式名稱。
  • param
    • 傳遞至函式的參數名稱。
  • statements
    • 組成該函式主體的陳述。

首先是 async 關鍵字,在函式前寫上 async, 表示該函式為一個非同步函式,並且會得到一個 Promise 物件的回應結果

let sayHi =  async () => 'Hello!';
sayHi() // 預期可以得到一個 Promise {<resolved>: "Hello!"} 的回應

然後我們就可以透過 then() 的方式取得最後結果

let sayHi =  async () => 'Hello!';
sayHi().then(res => console.log(res)) 

再來是 await 關鍵字,當這個函式被呼叫時,透過撰寫在 statements 裡面的 await 接收一個 Promise 物件,並且會等到解析完當前Promise 物件的狀態(resolvereject)後才會繼續往下執行

let sayHi =  async () => await Promise.resolve('Hello');
sayHi() 

這邊要注意的是 await 會中斷 async 函式的執行直到解析完Promise 物件的狀態後才往下執行。

所以今天如果有多個 非同步函式使用 async/await 的話,秒數則為加總後的時間。

來看看這個測試例子:

function runTime(time, person) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(`${person}`);
    }, time);
  });
}

async function getTimes() {
  console.time();
  const result = await runTime(1000, 'Bill');
  const result2 = await runTime(3000, 'Jack');
  console.timeEnd();
}
  
getTimes();

透過設定 console.time()console.timeEnd() 計算使用 async/await 的非同步函式的總執行時間。會發現約4000毫秒(約4秒),因為 async/await是一個執行完成後再執行下一個的(串行執行)。

Day29-1

那問題來了,如果要能並行執行呢?

還記得前一篇提到的 Promise.all 吧?

這裡就要透過 async/await 搭配 Promise.all,來看看測試例子:

function runTime(time, person) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(`${person}`);
    }, time);
  });
}

async function getTimes() {
    console.time();
    const result = await Promise.all([runTime(1000, 'Bill'),runTime(3000, 'Jack')]);
    console.timeEnd();
  }
  
getTimes();

從得到的結果可以發現,使用 Promise.all 可以並行執行,進而縮短執行時間。

Day29-2

實作: 透過 async/await 搭配 fetch 介接高雄市資料開放平臺的 open data

最後以介接 open data的實作結束這一回合~

  1. 撰寫HTML、CSS部分
<!-- css part -->
<style>
  body {
    background: #f0d0d0;
  }
  h1 {
    text-align: center;
    font-weight: bold;
    font-size: 48px;
  }
  .charge-list {
    display: flex;
    list-style: none;
    flex-wrap: wrap;
  }
  .charge-list li {
    box-sizing: border-box;
    flex: 0 1 24%;
    padding: 10px;
    margin: 0 1% 2% 0;
    border-radius: 10px;
    transition: all .4s;
  }
  .charge-list li:hover {
    margin-top: -1%;
  }
  .charge-list li:nth-child(n) {
    background: #aaaadd;
  }
  .charge-list li:nth-child(2n) {
    background: #ddddaa;
  }
  .charge-list li:nth-child(3n) {
    background: #dad;
  }
  .charge-list li:nth-child(3n+1) {
    background: #aaf;
  }
</style>
<!-- html -->
<ul class="charge-list"></ul>
  1. 透過 async/await 搭配 fetch 介接open data
fetchData = endPoint => fetch(endPoint).then(res => res.json());

(async function getData() 

  const endPoint = 'https://data.kcg.gov.tw/dataset/a98754a3-3446-4c9a-abfc-58dc49f2158c/resource/48d4dfc4-a4b2-44a5-bdec-70f9558cd25d/download/yopendata1070622opendatajson-1070622.json';

  try {
    let chargeStation = await fetchData(endPoint);
    createDomElement(chargeStation);
  } 
  catch(e){
    console.log(e);
  } 
})();
  1. 將指定的資料內容(位置、地址)渲染到HTML頁面中
function createDomElement(chargeStation) {
  const domElements = chargeStation.map(place => {
    return `
    <li>
      <p class="location">位置: ${ place.Location }</p>
      <p class="address">地址:${ place.Address }</p>
    </li>
  `;
  }).join("");
  
  const chargeList = document.querySelector('.charge-list');
  chargeList.innerHTML = domElements;
}

今天就先到這裡囉~

明天見~


上一篇
使用 fetch() 來介接 api 吧!
下一篇
關於完賽 - 是逗點,不是句點
系列文
JavaScript 試煉之旅30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言