Skip to content

Commit 9a6a9fb

Browse files
authored
Update article.md
1 parent f2078b1 commit 9a6a9fb

File tree

1 file changed

+27
-9
lines changed

1 file changed

+27
-9
lines changed

1-js/11-async/06-promisify/article.md

+27-9
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
Such transformations are often required in real-life, as many functions and libraries are callback-based. But promises are more convenient, so it makes sense to promisify them.
66

7+
For better understanding, let's see an example.
8+
79
For instance, we have `loadScript(src, callback)` from the chapter <info:callbacks>.
810

911
```js run
@@ -21,8 +23,15 @@ function loadScript(src, callback) {
2123
// loadScript('path/script.js', (err, script) => {...})
2224
```
2325

24-
Let's promisify it. The new `loadScriptPromise(src)` function achieves the same result, but it accepts only `src` (no `callback`) and returns a promise.
26+
The function loads a script with the given `src`, and then calls `callback(err)` in case of an error, or `callback(null, script)` in case of successful loading. That's a widespread agreement for using callbacks, we saw it before.
27+
28+
Let's promisify it.
29+
30+
We'll make a new function `loadScriptPromise(src)`, that does the same (loads the script), but returns a promise instead of using callbacks.
31+
32+
In other words, we pass it only `src` (no `callback`) and get a promise in return, that resolves with `script` when the load is successful, and rejects with the error otherwise.
2533

34+
Here it is:
2635
```js
2736
let loadScriptPromise = function(src) {
2837
return new Promise((resolve, reject) => {
@@ -37,19 +46,19 @@ let loadScriptPromise = function(src) {
3746
// loadScriptPromise('path/script.js').then(...)
3847
```
3948

40-
Now `loadScriptPromise` fits well in promise-based code.
49+
As we can see, the new function is a wrapper around the original `loadScript` function. It calls it providing its own callback that translates to promise `resolve/reject`.
4150

42-
As we can see, it delegates all the work to the original `loadScript`, providing its own callback that translates to promise `resolve/reject`.
51+
Now `loadScriptPromise` fits well in promise-based code. If we like promises more than callbacks (and soon we'll see more reasons for that), then we will use it instead.
4352

44-
In practice we'll probably need to promisify many functions, so it makes sense to use a helper. We'll call it `promisify(f)`: it accepts a to-promisify function `f` and returns a wrapper function.
53+
In practice we may need to promisify more than one function, so it makes sense to use a helper.
4554

46-
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:
55+
We'll call it `promisify(f)`: it accepts a to-promisify function `f` and returns a wrapper function.
4756

4857
```js
4958
function promisify(f) {
50-
return function (...args) { // return a wrapper-function
59+
return function (...args) { // return a wrapper-function (*)
5160
return new Promise((resolve, reject) => {
52-
function callback(err, result) { // our custom callback for f
61+
function callback(err, result) { // our custom callback for f (**)
5362
if (err) {
5463
reject(err);
5564
} else {
@@ -69,11 +78,18 @@ let loadScriptPromise = promisify(loadScript);
6978
loadScriptPromise(...).then(...);
7079
```
7180

72-
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.
81+
The code may look a bit complex, but it's essentially the same that we wrote above, while promisifying `loadScript` function.
82+
83+
A call to `promisify(f)` returns a wrapper around `f` `(*)`. That wrapper returns a promise and forwards the call to the original `f`, tracking the result in the custom callback `(**)`.
84+
85+
Here, `promisiefy` assumes that the original function expects a callback with exactly 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.
7386

7487
But what if the original `f` expects a callback with more arguments `callback(err, res1, res2, ...)`?
7588

76-
Here's a more advanced version of `promisify`: if called as `promisify(f, true)`, the promise result will be an array of callback results `[res1, res2, ...]`:
89+
We can improve our helper. Let's make a more advanced version of `promisify`.
90+
91+
- When called as `promisify(f)` it should work similar to the version above.
92+
- When called as `promisify(f, true)`, it should return the promise that resolves with the array of callback results. That's exactly for callbacks with many arguments.
7793

7894
```js
7995
// promisify(f, true) to get array of results
@@ -101,6 +117,8 @@ f = promisify(f, true);
101117
f(...).then(arrayOfResults => ..., err => ...)
102118
```
103119
120+
As you can see it's essentially the same as above, but `resolve` is called with only one or all arguments depending on whether `manyArgs` is truthy.
121+
104122
For more exotic callback formats, like those without `err` at all: `callback(result)`, we can promisify such functions manually without using the helper.
105123
106124
There are also modules with a bit more flexible promisification functions, e.g. [es6-promisify](https://github.com/digitaldesignlabs/es6-promisify). In Node.js, there's a built-in `util.promisify` function for that.

0 commit comments

Comments
 (0)