JavaScript Promisification
Promisification is a long term for a straightforward transformation. It represents a conversion of the function, which accepts a callback into a function returning a promise.
Often, transformations like that are needed in real-life because multiple libraries and functions are based on callbacks. But, promises are much more convenient.
Let’s consider a load_Script(src, callback):
function load_Script(src, callback) {
let scriptJS = document.createElement('script');
scriptJS.src = src;
scriptJS.onload = () => callback(null, scriptJS);
scriptJS.onerror = () => callback(new Error(`Script load error for ${src}`));
document.head.append(scriptJS);
}
// usage:
// load_Script('path/script.js', (err, scriptJS) => {...})
Now, we are going to promisify it. The new load_Script_Promise(src) will get the same result accepting merely src. It returns a promise, like this:
let load_Script_Promise = function (src) {
return new Promise((resolve, reject) => {
load_Script(src, (err, script) => {
if (err) reject(err)
else resolve(script);
});
})
}
// usage:
// load_Script_Promise('path/script.js').then(...)
So, it can be stated that ow load_Script_Promise suits well in a promise-based code. It delegates the overall work to the original load_Script and provides its callback, which translates to promise resolve/reject.
Now, it’s necessary to use a helper. It can be called promisify(fn). It includes a to-promisify function f, returning a wrapper function.
That wrapper does the same as the code, described above. It returns a promise and passes the call to the original fn.
The result is tracked in a custom callback, like here:
function promisify(fn) {
return function (...args) { // return wrapper function
return new Promise((resolve, reject) => {
function callback(err, result) { // our custom callback for fn
if (err) {
reject(err);
} else {
resolve(result);
}
}
args.push(callback); // append custom callback at the end of the fn arguments
fn.call(this, ...args); // call the original function
});
};
};
// usage:
let load_Script_Promise = promisify(load_Script);
load_Script_Promise(...).then(...);
In the example above, the function waits for a callback along with two arguments such as result and err. So, when the callback is in the correct format, the promisify operates more efficiently.
Now, let’s discover a more complicated version of the promisify. After calling promisify(fn, true), the result of the promise will be an array of callback results, like this:
// promisify(fn, true) get an array of results
function promisify(fn, manyArgs = false) {
return function (...args) {
return new Promise((resolve, reject) => {
function callback(err, ...results) { // our custom callback for fn
if (err) {
reject(err);
} else {
resolve(manyArgs ? results : results[0]); // resolve with all callback results if manyArgs is specified
}
}
args.push(callback);
fn.call(this, ...args);
});
};
};
// usage:
fn = promisify(fn, true);
fn(...).then(arrayOfResults => ..., err => ...)
Also, there exist modules that have more flexible functions for promisification.
A built-in function, called util.promisify is used in Node.js.
Summary¶
Promisification is a useful and widespread approach that makes a developer’s life more productive. It is especially great while using it with async/await. However, the complete replacement for callbacks is not recommended.
It is important to note that a promise can have a single result. On the contrary, a callback can be called multiple times.
So, promisification is used for the functions, calling the callback once. Upcoming calls are always ignored.
0 Comments
CAN FEEDBACK
Emoji