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 函式。