Promisification – is a long word for a simple transform. It’s conversion of a function that accepts a callback into a function returning a promise.
To be more precise, we create a wrapper-function that does the same, internally calling the original one, but returns a promise.
Such transforms are often needed in real-life, as many functions and libraries are callback-based. But promises are more convenient. So it makes sense to promisify those.
For instance, we have loadScript(src, callback)
from the chapter Callback fonksiyonlarına giriş.
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);
}
// usage:
// loadScript('path/script.js', (err, script) => {...})
Let’s promisify it. The new loadScriptPromise(src)
function will do the same, but accept only src
(no callback) and return a promise.
let loadScriptPromise = function(src) {
return new Promise((resolve, reject) => {
loadScript(src, (err, script) => {
if (err) reject(err)
else resolve(script);
});
})
}
// usage:
// loadScriptPromise('path/script.js').then(...)
Now loadScriptPromise
fits well in our promise-based code.
As we can see, it delegates all the work to the original loadScript
, providing its own callback that translates to promise resolve/reject
.
As we may need to promisify many functions, it makes sense to use a helper.
That’s actually very simple – promisify(f)
below takes a to-promisify function f
and returns a wrapper function.
That wrapper does the same as in the code above: returns a promise and passes the call to the original f
, tracking the result in a custom callback:
function promisify(f) {
return function (...args) { // return a wrapper-function
return new Promise((resolve, reject) => {
function callback(err, result) { // our custom callback for f
if (err) {
return reject(err);
} else {
resolve(result);
}
}
args.push(callback); // append our custom callback to the end of arguments
f.call(this, ...args); // call the original function
});
};
};
// usage:
let loadScriptPromise = promisify(loadScript);
loadScriptPromise(...).then(...);
Here we assume that the original function expects a callback with two arguments (err, result)
. That’s what we encounter most often. Then our custom callback is in exactly the right format, and promisify
works great for such a case.
But what if the original f
expects a callback with more arguments callback(err, res1, res2)
?
Here’s a modification of promisify
that returns an array of multiple callback results:
// promisify(f, true) to get array of results
function promisify(f, manyArgs = false) {
return function (...args) {
return new Promise((resolve, reject) => {
function callback(err, ...results) { // our custom callback for f
if (err) {
return reject(err);
} else {
// resolve with all callback results if manyArgs is specified
resolve(manyArgs ? results : results[0]);
}
}
args.push(callback);
f.call(this, ...args);
});
};
};
// usage:
f = promisify(f, true);
f(...).then(arrayOfResults => ..., err => ...)
In some cases, err
may be absent at all: callback(result)
, or there’s something exotic in the callback format, then we can promisify such functions without using the helper, manually.
There are also modules with a bit more flexible promisification functions, e.g. es6-promisify. In Node.js, there’s a built-in util.promisify
function for that.
Promisification is a great approach, especially when you use async/await
(see the next chapter), but not a total replacement for callbacks.
Remember, a promise may have only one result, but a callback may technically be called many times.
So promisification is only meant for functions that call the callback once. Further calls will be ignored.
Yorumlar
<code>
kullanınız, birkaç satır eklemek için ise<pre>
kullanın. Eğer 10 satırdan fazla kod ekleyecekseniz plnkr kullanabilirsiniz)