iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 28
0
Modern Web

從零開始進入 JavaScript & TypeScript 的世界系列 第 28

第28天-Promise在非同步的應用

  • 分享至 

  • xImage
  •  

非同步執行最迷人的一點就是我們可以讓他並行(Parallel), 加快我們程式的速度!例如我們要下載 3 本書的資訊,每讀取一本需要花費一秒鐘,讀取三本就需要三秒鐘。但,如果在頻寬允許的情況下,我們一次讀取三本,那就只需要花費一秒鐘。

今天要學習的就是使用 Promise 達到並行的目的!

首先,我們先準備一個假的 API 讓我們讀取書本的資訊:

// 檔名: getBooksDetails.ts
export type bookIndex = 'imposter_cure'|'atomic_habits'|'evil_girl';

const books = {
  imposter_cure: {
    name: '冒牌者症候群',
    author: '潔薩米.希伯德',
    price: 221,
    publisher: '商周出版',
    url: 'https://readmoo.com/book/210117591000101',
  },
  atomic_habits: {
    name: '原子習慣',
    author: '詹姆斯.克利爾',
    price: 231,
    publisher: '方智出版',
    url: 'https://readmoo.com/book/210109063000101',
  },
  evil_girl: {
    name: '末日前,我把惡魔少女誘拐回家了',
    author: '黑貓C',
    price: 231,
    publisher: '奇幻基地',
    url: 'https://readmoo.com/book/210117051000101',
  }
}

export async function getBooksDetails(handle: bookIndex) {
  return new Promise<{
    name: string,
    author: string,
    price: number,
    publisher: string,
    url: string
  }>(resolve => {
    setTimeout(() => {
      resolve(books[handle]);
    }, 1500);
  });
}

在這個假的 API 中,有一個關鍵自是我們之前沒有介紹的,那就是export。關於 export, 他的目的就是要讓其他的 ts 可以在 import 它之後可以存取到它。

也就是說,當其他人 import 這個 getBooksDetails.ts 後,他只能存取到 bookIndexgetBookDetails, 由於 books 沒有 export, 儘管他不是 private, 其他 import getBooksDetails.ts 的人也無法存取到 books

首先,如果一筆一筆資料讀取的話,我們會使用一個 for 迴圈把所有想要的資料讀取完畢!但在那之前,我們必須要先把這個假的 API 給 Import 進來。

import {getBooksDetails} from './getBooksDetail';
import {bookIndex} from './getBooksDetail';

接著宣告一個陣列,裡面存放所有我們想要讀取的圖書資 Index:

const books: Array<bookIndex> = ['imposter_cure', 'atomic_habits', 'evil_girl'];

最後,我們就可以寫一個非同步的函式,來讀取所以要想的書籍資料:

async function readBooksDetails() {
  for (const b of books) {
    const item = await getBooksDetails(b);
    console.log('-------------------------------------')
    console.log(
        '書名:' + item.name + '\n' +
        '作者:' + item.author + '\n' +
        '價格:' + item.price + '\n' +
        '出版社:' + item.publisher + '\n' +
        '網址:' + item.url);
    console.log('-------------------------------------')
  }
}

readBooksDetails();

不過,還記得嗎?我們假設每讀取一本書籍資訊需要花費時間一秒鐘,所以,這個迴圈跑完一共會需要三秒鐘。那如果我想要在一秒鐘就取得三本書的資料該怎麼做呢?

Promise.all

這時候我們就可以使用 Promise.all 達到並行, 同時讀三本書,假設每一本都是需要花一秒,所以,一口氣同時讀取三本,最後只需要花一秒鐘就可以完成了。

// 先透過 map function 取得三本書的 promise
const allPromises = books.map(getBooksDetails);

// 再使用 Promise.all 把所有的 promise 合併起來
const combinedPromise = Promise.all(allPromises);

// 最後執行它, 我們只會花一秒鐘就可以得到 details 了
combinedPromise.then(
    values => {
      console.log(values);
    },
    reason => {
      console.log('Error:' + reason);
    });
}

輸出的結果如下:

[ { name: '冒牌者症候群',
    author: '潔薩米.希伯德',
    price: 221,
    publisher: '商周出版',
    url: 'https://readmoo.com/book/210117591000101' },
  { name: '原子習慣',
    author: '詹姆斯.克利爾',
    price: 231,
    publisher: '方智出版',
    url: 'https://readmoo.com/book/210109063000101' },
  { name: '末日前,我把惡魔少女誘拐回家了',
    author: '黑貓C',
    price: 231,
    publisher: '奇幻基地',
    url: 'https://readmoo.com/book/210117051000101' } ]

Promise.race

在學習 Promise.all 的過程中,意外看到 Promise.race, 雖然目前還沒想到使用他的時機點,但這個也是滿有趣的。所以就一並在這邊跟大家分享。

關於 Promise.race 它的功能就是,當我們去並行數個 promise 時,他只會回傳遞一個回來的 promise 的結果!也就是說,我們同樣以上面書本的例子來看,假設每一本書回覆的時間是不固定的,有時快有時慢,但都在一秒上下!這時候,如果我們使用 Promise.race, 我們得到的結果就有可能是第一本書,也有可能是第二本書,也有可能是第三本書,就看哪一筆資料回覆的時間最快,結果就會是哪一筆。

他的操作方式與 Promise.all 差不多,就只是結果不一樣而已!

const resultOfPromiseThatWins = Promise.race(allPromises);
resultOfPromiseThatWins.then(
    value => {
        console.log('Promise with Race: ' + value.name)
    },
    reason => {
        console.log('Reject:' + reason)
    }
);

而他輸出的結果,書本的名稱就會是不一定,看誰快就是誰!

Promise with Race: 冒牌者症候群

上一篇
第27天-非同步和 Promise (async & await)
下一篇
第29天-Truthy和Falsy
系列文
從零開始進入 JavaScript & TypeScript 的世界30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言