Update article.md
This commit is contained in:
parent
f2078b1d80
commit
9a6a9fb584
1 changed files with 27 additions and 9 deletions
|
@ -4,6 +4,8 @@
|
|||
|
||||
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.
|
||||
|
||||
For better understanding, let's see an example.
|
||||
|
||||
For instance, we have `loadScript(src, callback)` from the chapter <info:callbacks>.
|
||||
|
||||
```js run
|
||||
|
@ -21,8 +23,15 @@ function loadScript(src, callback) {
|
|||
// loadScript('path/script.js', (err, script) => {...})
|
||||
```
|
||||
|
||||
Let's promisify it. The new `loadScriptPromise(src)` function achieves the same result, but it accepts only `src` (no `callback`) and returns a promise.
|
||||
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.
|
||||
|
||||
Let's promisify it.
|
||||
|
||||
We'll make a new function `loadScriptPromise(src)`, that does the same (loads the script), but returns a promise instead of using callbacks.
|
||||
|
||||
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.
|
||||
|
||||
Here it is:
|
||||
```js
|
||||
let loadScriptPromise = function(src) {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
@ -37,19 +46,19 @@ let loadScriptPromise = function(src) {
|
|||
// loadScriptPromise('path/script.js').then(...)
|
||||
```
|
||||
|
||||
Now `loadScriptPromise` fits well in promise-based code.
|
||||
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`.
|
||||
|
||||
As we can see, it delegates all the work to the original `loadScript`, providing its own callback that translates to promise `resolve/reject`.
|
||||
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.
|
||||
|
||||
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.
|
||||
In practice we may need to promisify more than one function, so it makes sense to use a helper.
|
||||
|
||||
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:
|
||||
We'll call it `promisify(f)`: it accepts a to-promisify function `f` and returns a wrapper function.
|
||||
|
||||
```js
|
||||
function promisify(f) {
|
||||
return function (...args) { // return a wrapper-function
|
||||
return function (...args) { // return a wrapper-function (*)
|
||||
return new Promise((resolve, reject) => {
|
||||
function callback(err, result) { // our custom callback for f
|
||||
function callback(err, result) { // our custom callback for f (**)
|
||||
if (err) {
|
||||
reject(err);
|
||||
} else {
|
||||
|
@ -69,11 +78,18 @@ 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.
|
||||
The code may look a bit complex, but it's essentially the same that we wrote above, while promisifying `loadScript` function.
|
||||
|
||||
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 `(**)`.
|
||||
|
||||
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.
|
||||
|
||||
But what if the original `f` expects a callback with more arguments `callback(err, res1, res2, ...)`?
|
||||
|
||||
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, ...]`:
|
||||
We can improve our helper. Let's make a more advanced version of `promisify`.
|
||||
|
||||
- When called as `promisify(f)` it should work similar to the version above.
|
||||
- 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.
|
||||
|
||||
```js
|
||||
// promisify(f, true) to get array of results
|
||||
|
@ -101,6 +117,8 @@ f = promisify(f, true);
|
|||
f(...).then(arrayOfResults => ..., err => ...)
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
For more exotic callback formats, like those without `err` at all: `callback(result)`, we can promisify such functions manually without using the helper.
|
||||
|
||||
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.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue