You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: 1-js/11-async/06-promisify/article.md
+27-9
Original file line number
Diff line number
Diff line change
@@ -4,6 +4,8 @@
4
4
5
5
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.
6
6
7
+
For better understanding, let's see an example.
8
+
7
9
For instance, we have `loadScript(src, callback)` from the chapter <info:callbacks>.
8
10
9
11
```js run
@@ -21,8 +23,15 @@ function loadScript(src, callback) {
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.
25
33
34
+
Here it is:
26
35
```js
27
36
letloadScriptPromise=function(src) {
28
37
returnnewPromise((resolve, reject) => {
@@ -37,19 +46,19 @@ let loadScriptPromise = function(src) {
37
46
// loadScriptPromise('path/script.js').then(...)
38
47
```
39
48
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`.
41
50
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.
43
52
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.
45
54
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.
47
56
48
57
```js
49
58
functionpromisify(f) {
50
-
returnfunction (...args) { // return a wrapper-function
59
+
returnfunction (...args) { // return a wrapper-function (*)
51
60
returnnewPromise((resolve, reject) => {
52
-
functioncallback(err, result) { // our custom callback for f
61
+
functioncallback(err, result) { // our custom callback for f (**)
53
62
if (err) {
54
63
reject(err);
55
64
} else {
@@ -69,11 +78,18 @@ let loadScriptPromise = promisify(loadScript);
69
78
loadScriptPromise(...).then(...);
70
79
```
71
80
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.
73
86
74
87
But what if the original `f` expects a callback with more arguments `callback(err, res1, res2, ...)`?
75
88
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.
77
93
78
94
```js
79
95
// promisify(f, true) to get array of results
@@ -101,6 +117,8 @@ f = promisify(f, true);
101
117
f(...).then(arrayOfResults=>..., err=>...)
102
118
```
103
119
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
+
104
122
For more exotic callback formats, like those without `err` at all: `callback(result)`, we can promisify such functions manually without using the helper.
105
123
106
124
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