promisification 是指將 callback-based 函式轉換成回傳 promise 的函式。先前文中曾嘗試把 loadScript
改成使用 promise,而在實務上如果有大量函式需要轉換,可以利用一個 helper function 來統一轉換工作。
以單一一個 callback 函式而言,一種簡單的轉換方式是不改變它的使用、直接在外面包一層函式回傳 promise,就像下方的 loadScriptPromise
。loadScriptPromise
本身不傳入 callback,而是在裡面執行原本的 loadScript
時,提供 callback 參數給它,讓它可以傳出 promise 結果。這樣一來就可以用原本的方式使用 loadScriptPromise(src)
載入檔案,然後函式也能相容在 promise-based 程式碼中。
// callback based
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`));
document.head.append(script);
}
// promisified
function loadScriptPromise(src) {
return new Promise((resolve, reject) => {
loadScript(src, (err, script) => {
if (err) {
reject(err);
} else {
resolve(script);
}
});
});
};
以上面的方式為基礎,可以建立一個 promisify(f)
來進行包裝轉換:
function promisify(f) {
return function (...args) {
return new Promise((resolve, reject) => {
function callback(err, result) { // 4
if (err) {
reject(err);
} else {
resolve(result);
}
}
args.push(callback); // 11
f.call(this, ...args); // 12
});
};
}
// 使用
let loadScriptPromise = promisify(loadScript);
loadScriptPromise(...).then(...);
promisify
參數傳入一個要轉換的 callback 函式 f
,然後會回傳一個 wrapper function,裡面做的就是如同前個例子中 loadScriptPromise
做的事:
f
的 callback,讓它可以 resolve/ reject (4)f
(12)利用 promisify
就可以不用逐一改寫原有的 callback 函式。