網站搜尋

如何讓 async.mapLimit 與 TypeScript 的 async/await 一起使用


是否可以使用 Async NPM 模組在 TypeScript 2.2x 中搭配 async/await 使用?

目標: 使用 Async 的 mapLimit 函數創建一個網頁爬蟲,啟動 10 個並行的 HTTP 請求。

包裝過的 HTTP 函數的範例如下:

async callUniRest(url: string): Promise<number> {
  return new Promise<number>(resolve => {
    unirest.get(url)
      .end((response: any) => {
          resolve(cheerio.load(response.body);
      });
    });
}

問題:

當我跑步時:

const myList: string[] = ['http...', 'http...', 'http...', 'http...']
async.mapLimit(myList, 10, callUniRest, function(err: any, results: any {
  console.log(results);
})

回呼函式只會在第一個元素完成後才被呼叫。

問題: 我如何啟用 async.mapLimit 以處理多個呼叫?

答案#1

是否可以使用 'async' npm 模組在 Typescript 中使用 async/await

是。

我發現 mapLimit 只對第一組調用迭代器。這是因為我正在從 TypeScript 轉譯。如果我在 JS 中使用原生的 async/await,它就能正常運作。這是因為,引用自 AsyncFunction 的 async 文檔:

由於 JavaScript 的限制,我們只能檢測原生的非同步函數,而無法檢測轉譯後的實現。

所以如果你用 async 庫的 asyncify 包裝你的迭代器,那麼你可以在迭代器內部返回,而不使用回調函數:

如果你是透過轉譯器(例如 Babel)使用 async 函數,你仍然必須將函數用 asyncify 包裹,因為 async 函數會被編譯成返回 Promise 的普通函數。

這裡有一個有點勉強的 TypeScript 範例,示範如何做到這一點。注意:如果你移除 asyncify 呼叫,它將無法運作:

import { asyncify, mapLimit } from "async";

const listOfThings = "abcdefghijklmnopqrstuvwxyz".split("");

const convertToUppercase = async () =>
  await mapLimit<string, string[]>(
    listOfThings,
    5,
    asyncify(async letter => letter.toUpperCase())
  );

const init = async () => {
  const uppered = await convertToUppercase();

  console.log("done", uppered);
};

init();

答案#2

不。async 模組為那些不能或不想使用 Promise 的人提供工具和抽象。它們不能混合使用。

更不用說,當你擁有 myList 的時候,所有請求已經都發送出去了。

async.mapLimit 僅適用於接受回呼函式的函式,而不適用於返回 Promise 的函式。

你可能想使用 Bluebird 的 Promise.map() 與 concurrency 操作符