Awesome Promise Methods

作者: Calpa Liu
字數:808
出版:2018 年 10 月 15 日
## promisify

promisify

無論什麼函數,我們也可以將它化為異步函數。

這裡使用 currying 來返回一個函數,再返回一個 Promise 來調用原本的函數。

而且,可以用 ...rest 來傳遞所有參數,而不是 arguments

const promisify =
  (func) =>
  (...args) =>
    new Promise((resolve, reject) =>
      func(...args, (err, result) => (err ? reject(err) : resolve(result)))
    );

使用方法

const delay = promisify((d, cb) => setTimeout(cb, d));
delay(2000).then(() => console.log("Hello World!"));

值得一提的是,如果你的 Node.js 版本為 8 以上,你可以使用 util.promisify

const util = require("util");
const fs = require("fs");

const stat = util.promisify(fs.stat);
stat(".")
  .then((stats) => {
    // 做你要做的事情
  })
  .catch((error) => {
    // 處理錯誤
  });

你也可以使用 async/await 版本:

const util = require("util");
const fs = require("fs");

const stat = util.promisify(fs.stat);

async function callStat() {
  const stats = await stat(".");
  console.log(`This directory is owned by ${stats.uid}`);
}

runAsync

使用 Web Worker 來在獨立線程調用函數,避免阻塞 UI 交互。

我們首先會使用Blob 對象鏈接來創建一個 Worker,裡面的內容是純字符串的函數。

然後,馬上使用這個 worker,並返回一個 Promise

監聽 onmessageonerror 事件,然後處理返回的 data。

代碼片段:

const runAsync = (fn) => {
  const worker = new Worker(
    URL.createObjectURL(new Blob([`postMessage((${fn})());`]), {
      type: "application/javascript; charset=utf-8",
    })
  );
  return new Promise((res, rej) => {
    worker.onmessage = ({ data }) => {
      res(data), worker.terminate();
    };
    worker.onerror = (err) => {
      rej(err), worker.terminate();
    };
  });
};

使用例子:

const longRunningFunction = () => {
  let result = 0;
  for (let i = 0; i < 1000; i++) {
    for (let j = 0; j < 700; j++) {
      for (let k = 0; k < 300; k++) {
        result = result + i + j + k;
      }
    }
  }
  return result;
};
runAsync(longRunningFunction).then(console.log); // 209685000000
runAsync(() => 10 ** 3).then(console.log); // 1000

值得注意的是,由於函數運行的環境不同,所有變量和函數都需要在裡面定義。

使用外部變量的話,會出現問題:

let outsideVariable = 50;
runAsync(() => typeof outsideVariable).then(console.log); // 'undefined'

同時執行 Promise

如果想要一次性地幾千個請求的話,使用 Promise.all() 方法就可以達到並發的效果了。

使用方法

const test = (time) =>
  new Promise((resolve) => {
    setTimeout(() => resolve(time), time);
  });

const temp = [test(1000), test(2000), test(3000)];

Promise.all(temp).then(console.log); // [1000, 2000, 3000]

runPromisesInSeries

如果你想要一個個串聯,運行一系列的 Promise的話,可以使用 Array.prototype.reduce() 來製造一個 Promise 的鏈。當下個 Promise resolved,返回上一個 Promise。

const runPromisesInSeries = (ps) =>
  ps.reduce((p, next) => p.then(next), Promise.resolve());

使用方法

const delay = (time) =>
  new Promise((resolve) => {
    console.log(time);
    setTimeout(resolve, time);
  });

const promiseSeries = [() => delay(1000), () => delay(2000)];

runPromisesInSeries(promiseSeries);

sleep

簡單來說,就是讓函數等若干秒,然後再執行下一步。

const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));

使用方法

async function sleepyWork() {
  console.log("等一秒");
  await sleep(1000);
  console.log("一秒過去了");
}

isPromiseLike

我們可以簡單容易地判斷一個目標是否類似 Promise

我們可以使用判斷對象是否為 null,而且 typeof 的返回值是 object 或者 function,而且它擁有 .then 的屬性。

const isPromiseLike = (obj) =>
  obj !== null &&
  (typeof obj === "object" || typeof obj === "function") &&
  typeof obj.then === "function";

使用方法

const promise = new Promise((resolve) => {
  resolve();
});

isPromiseLike(promise); // true

isPromiseLike({
  then: function () {
    return "";
  },
}); // true
isPromiseLike(null); // false
isPromiseLike({}); // false

值得注意的是:

這個方法只是一個判斷類似 Promise,並非完全等於 Promise

const fakePromise = {
  then: () => {},
};

isPromiseLike(fakePromise); // true

One more thing

上面提到的方法,都是從 30-seconds-of-code ,裡面提到很多可以在三十秒內閱讀,開箱即用的方法。

如果有時間的話,可以研讀一下裡面的代碼。

參考資料

  1. 30-seconds-of-code
  2. Promise.all() - MDN web docs
關於 Calpa

Calpa 擅長使用 TypeScriptReact.jsVue.js 建立 Responsive Website。

他積極參與開源社區,曾在 2019 年的香港開源大會上擔任講者,提供工作經驗和見解。此外,他也在 GitHub 上公開分享個人博客程式碼,已獲得超過 300 顆星星和 60 個分支的支持。

他熱愛學習新技術,並樂意分享經驗。他相信,唯有不斷學習才能跟上快速演變的技術環境。

熱門文章

最新文章

圖片管理中心
管理圖片資源
IP 查詢
快速查詢和定位 IP 地址的地理位置和相關信息
Python 運行器
無需後端、無需登入,只需打開瀏覽器即可運行 Python 代碼(由 Pyodide 提供支持)
封面圖生成器
自動創建適合各種平台的文章封面圖
原作(青山剛昌)產生器
一鍵創建原作(青山剛昌)的封面圖
日本色彩
探索和使用傳統日本色彩
部落格內容洞察儀表板
以視覺化儀表板方式追蹤文章成效、分享熱度與分類分布,協助創作者掌握內容表現。
蒙特卡羅估算 π
使用蒙特卡羅方法演示 π 值的估算過程
LLM
使用 LLM 模型進行聊天
活動圖生成器
一鍵創建活動的封面圖
Wagmi Card
一鍵創建 Wagmi 的封面圖
Facebook Quote
Facebook Quote
Music Macro Language (MML) Studio
用程式語法編寫旋律,用音符構築想像
Blurhash
一鍵創建 Blurhash
文字分類器
使用 MediaPipe TextClassifier 分類文字
前端工程師免費工具資源
前端工程師免費工具資源
後端工程師免費工具資源
後端工程師免費工具資源
全端工程師免費工具資源
全端工程師免費工具資源
Web3 工程師免費工具資源
Web3 工程師免費工具資源
紫微斗數排盤系統|結合 AI 的命盤性格與事業財務分析生成器
紫微斗數排盤工具,輸入生日與時辰,自動生成完整命盤分析提示(Prompt)。結合最專業紫微理論與 AI 助力,助你深入解析性格、事業、財務與人際課題。免費使用,適合命理師及紫微愛好者。
PixAI Prompt 組合器|快速打造可用於 AI 繪圖的語言拼圖
使用 PixAI 卻不會寫 prompt?這個工具幫你一鍵組裝角色、表情、風格語彙,輸出高品質繪圖提示語句(Prompt),可直接貼入 PixAI 使用。適合插畫師、創作者、AI 新手與 VTuber 角色開發者。