99re热这里只有精品视频,7777色鬼xxxx欧美色妇,国产成人精品一区二三区在线观看,内射爽无广熟女亚洲,精品人妻av一区二区三区

Javascript Promisification

2023-02-17 10:53 更新

對于一個簡單的轉換來說 “Promisification” 是一個長單詞。它指將一個接受回調的函數轉換為一個返回 promise 的函數。

由于許多函數和庫都是基于回調的,因此,在實際開發(fā)中經常會需要進行這種轉換。因為使用 promise 更加方便,所以將基于回調的函數和庫 promise 化是有意義的。

為了更好地理解,讓我們來看一個例子。

例如,在 簡介:回調 一章中我們有 loadScript(src, callback)

function loadScript(src, callback) {
  let script = document.createElement('script');
  script.src = src;

  script.onload = () => callback(null, script);
  script.onerror = () => callback(new Error(`Script load error for ${src}`));

  document.head.append(script);
}

// 用法:
// loadScript('path/script.js', (err, script) => {...})

該函數通過給定的 src 加載腳本,然后在出現錯誤時調用 callback(err),或者在加載成功時調用 callback(null, script)。這是大家對于使用回調函數的共識,我們之前也學習過。

現在,讓我們將其 promise 化吧。

我們將創(chuàng)建一個新的函數 loadScriptPromise(src),與上面的函數作用相同(加載腳本),只是我們創(chuàng)建的這個函數會返回一個 promise 而不是使用回調。

換句話說,我們僅向它傳入 src(沒有 callback)并通過該函數的 return 獲得一個 promise,當腳本加載成功時,該 promise 將以 script 為結果 resolve,否則將以出現的 error 為結果 reject。

代碼實現如下:

let loadScriptPromise = function(src) {
  return new Promise((resolve, reject) => {
    loadScript(src, (err, script) => {
      if (err) reject(err);
      else resolve(script);
    });
  });
};

// 用法:
// loadScriptPromise('path/script.js').then(...)

正如我們所看到的,新的函數是對原始的 loadScript 函數的包裝。新函數調用它,并提供了自己的回調來將其轉換成 promise resolve/reject。

現在 loadScriptPromise 非常適用于基于 promise 的代碼了。如果我們相比于回調函數,更喜歡 promise(稍后我們將看到更多喜歡 promise 的原因),那么我們將改用它。

在實際開發(fā)中,我們可能需要 promise 化很多函數,所以使用一個 helper(輔助函數)很有意義。

我們將其稱為 promisify(f):它接受一個需要被 promise 化的函數 f,并返回一個包裝(wrapper)函數。

function promisify(f) {
  return function (...args) { // 返回一個包裝函數(wrapper-function) (*)
    return new Promise((resolve, reject) => {
      function callback(err, result) { // 我們對 f 的自定義的回調 (**)
        if (err) {
          reject(err);
        } else {
          resolve(result);
        }
      }

      args.push(callback); // 將我們的自定義的回調附加到 f 參數(arguments)的末尾

      f.call(this, ...args); // 調用原始的函數
    });
  };
}

// 用法:
let loadScriptPromise = promisify(loadScript);
loadScriptPromise(...).then(...);

代碼看起來可能有些復雜,但其本質與我們在上面寫的那個是一樣的,就是將 loadScript 函數 promise 化。

調用 promisify(f) 會返回一個 f (*) 的包裝器。該包裝器返回一個 promise,并將調用轉發(fā)給原始的 f,并在我們自定義的回調 (**) 中跟蹤結果。

在這里,promisify 假設原始函數期望一個帶有兩個參數 (err, result) 的回調。這就是我們最常遇到的形式。那么我們自定義的回調的格式是完全正確的,在這種情況下 promisify 也可以完美地運行。

但是如果原始的 f 期望一個帶有更多參數的回調 callback(err, res1, res2, ...),該怎么辦呢?

我們可以繼續(xù)改進我們的輔助函數。讓我們寫一個更高階版本的 promisify

  • 當它被以 ?promisify(f)? 的形式調用時,它應該以與上面那個版本的實現的工作方式類似。
  • 當它被以 ?promisify(f, true)? 的形式調用時,它應該返回以回調函數數組為結果 resolve 的 promise。這就是具有很多個參數的回調的結果。
// promisify(f, true) 來獲取結果數組
function promisify(f, manyArgs = false) {
  return function (...args) {
    return new Promise((resolve, reject) => {
      function callback(err, ...results) { // 我們自定義的 f 的回調
        if (err) {
          reject(err);
        } else {
          // 如果 manyArgs 被指定,則使用所有回調的結果 resolve
          resolve(manyArgs ? results : results[0]);
        }
      }

      args.push(callback);

      f.call(this, ...args);
    });
  };
}

// 用法:
f = promisify(f, true);
f(...).then(arrayOfResults => ..., err => ...);// promisify(f, true) 來獲取結果數組
function promisify(f, manyArgs = false) {
  return function (...args) {
    return new Promise((resolve, reject) => {
      function callback(err, ...results) { // 我們自定義的 f 的回調
        if (err) {
          reject(err);
        } else {
          // 如果 manyArgs 被指定,則使用所有回調的結果 resolve
          resolve(manyArgs ? results : results[0]);
        }
      }

      args.push(callback);

      f.call(this, ...args);
    });
  };
}

// 用法:
f = promisify(f, true);
f(...).then(arrayOfResults => ..., err => ...);// promisify(f, true) 來獲取結果數組
function promisify(f, manyArgs = false) {
  return function (...args) {
    return new Promise((resolve, reject) => {
      function callback(err, ...results) { // 我們自定義的 f 的回調
        if (err) {
          reject(err);
        } else {
          // 如果 manyArgs 被指定,則使用所有回調的結果 resolve
          resolve(manyArgs ? results : results[0]);
        }
      }

      args.push(callback);

      f.call(this, ...args);
    });
  };
}

// 用法:
f = promisify(f, true);
f(...).then(arrayOfResults => ..., err => ...);

正如你所看到的,它與上面那個實現基本相同,只是根據 manyArgs 是否為真來決定僅使用一個還是所有參數調用 resolve。

對于一些更奇特的回調格式,例如根本沒有 err 的格式:callback(result),我們可以手動 promise 化這樣的函數,而不使用 helper。

也有一些具有更靈活一點的 promisification 函數的模塊(module),例如 es6-promisify。在 Node.js 中,有一個內建的 promise 化函數 util.promisify。

請注意:

Promisification 是一種很好的方法,特別是在你使用 async/await 的時候(請看下一章),但不是回調的完全替代。

請記住,一個 promise 可能只有一個結果,但從技術上講,一個回調可能被調用很多次。

因此,promisification 僅適用于調用一次回調的函數。進一步的調用將被忽略。


以上內容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號