Permalink
Browse files

Added node-api-promises notes

  • Loading branch information...
deconstructionalism
deconstructionalism committed Dec 11, 2018
1 parent d0ae93b commit eb50d2cf3858bf56582130392078c30715214388
@@ -0,0 +1,192 @@
## LESSON: [node-api-promises](https://git.generalassemb.ly/ga-wdi-boston/node-api-promises)

---

**2018-12-09**

### OBJECTIVES

By the end of this, developers should be able to:

- Explain the value of using promises instead of callback interfaces.
- Read Node documentation that uses callbacks and translate that into
implementations using promises.
- Rewrite Node scripts using callbacks as scripts using promises.

### VOCABULARY

**Synchronous Function**: Any function that will wait to complete before continuing running any code. Synchronous functions are called **blocking** in that they block the process in which they are invoked from continuing until they are resolved.

**Asynchronous Function**: Any function which will not resolve immediately after it is invoked. Asynchronous functions are called **non-blocking** in that they do not block the process in which they are invoked from continuing. The way `node` handle asynchronous functions is allowing them to split off and run on their own process, while the original process that original invoked the asynchronous function can keep running. This can make handling asynchronous functions and dealing with their outputs more complex than with synchronous functions.

**Callback Function**: A function passed into another function as one of it's arguments. A callback function can be run from within the function it is passed into, which allows for us to wait for an asynchronous process to complete within the function, and then once complete, run the callback function. The callback function is often passing the resulting data from the asynchronous process.

**Blocking**: An expression that blocks the process of running code, and will wait for that expression to resolve before continuing.

**Non-blocking**: An expression that does not block the process of running code, and will start running that expression on a separate process, while continuing to run the code after that expression.

**Promise**: An object that allows us to do something asynchronous and then easily attach callbacks to run in the case of success or failure for that asynchronous process. The `Promise` constructor allows us to create these objects. This asynchronous action is specified as the only argument to the `Promise` constructor, a callback function called the `executor`. The `.then` and `.catch` methods available on each `Promise` instance allow us to easily attach callback functions to handle success and failure of the `Promise`, respectively.

**Executor Function**: The only argument to a `Promise` constructor, which executes some asynchronous task, then reports back whether the task completed successfully or not. The executor always takes in two parameters, `resolve` and `reject`. When you make a new `Promise` object, it runs the executor function and passes it two functions, `resolve` an `reject`, which the executor can use within it's function block. The executor can indicate a successful resolution of the task (by calling `resolve`) or a failure of the task (by calling `reject`). Calling `resolve` will take whatever data is passed into the `resolve` function and then run the callback specified in `then` method of the `Promise` with that data as the argument. Calling `reject` will take whatever data is passed into the `reject` function and then run the callback specified in `catch` method of the `Promise` with that data as the argument.

### NOTES

- Many tasks we want to run in `node` execute asynchronously, such as server requests, reading files, writing files, etc. We cannot simply put consecutive commands in order in our code when one of them is asynchronous, because node will start the asynchronous process and move on and run the next lines before the asynchronous process has completed. Often, we need tasks to run in a particular order, regardless of whether they are asynchronous or synchronous. How can we ensure that tasks run in the desired order?

- `Promise`s allow for asynchronous actions to be handled more cleanly than callbacks alone, allowing for much easier comprehension of execution order and unified error handling.

- There is only one error handling function specified using `.catch`
- You can chain `.then` callbacks one after another and they will run strictly in that order, waiting for each previous callback to finish before running the next one
- the result of a previous `.then` callback will automatically be piped into the input of the next `.then` callback
- if an error occurs along the way, the `.catch` callback will be run and receive the error object as it's argument
- ![control flow in promises](./images/control-flow-promises.jpg)



- In many cases, you will be dealing with functions that are already built to return a `Promise` to you, so you can simply add `.then` and `.catch` methods to it as follows:

- ```js
asyncPromiseFunction()
.then(successCallback)
.catch(failureCallback)
```

- this is exactly what we did with `$.ajax()` in our projects

- You can chain multiple `.then` callbacks to run:

- ```js
asyncPromiseFunction()
.then(firstThing)
.then(secondThing)
.then(thirdThing)
.catch(failureCallback)
```

- When you want to write your own `Promise` returning function, you should always stick to this skeleton:

```js
const promiseSkeletonFunction = (<args>) => {
return new Promise(resolve, reject) => {
//some async thing that returns a result
if (<result is failure>) {
reject(<failure data>)
} else {
resolve(<success data>)
}
}
}
```



### EXAMPLES

#### Callbacks Are Useful to Handle Asynchronicity

- `node` runs things asynchronously by default. We'll see that it does not wait for asynchronous things to finished before running the next line.

- ```js
/* We want the code below to wait 4 seconds then log 'almost...', then '...done'
*/
// an asynchronous function
const asynchFunctionWithoutCallback = () => {
// this will console.log the text after 4 seconds
setTimeout(() => {
console.log('almost...')
}, 4000)
}
// another function
const nextFunction = () => {
console.log('I\'m done')
}
// we want these to print in the order they are run
asynchFunctionWithoutCallback()
nextFunction()
// => "I'm done
// waits 4 seconds...
// => "almost..."
// :-(
```


- In order to get the functions to execute in the right order, we can use callbacks:

- ```js
// an asynchronous function w/ a callback
const asynchFunctionWithCallback = (callback) => {
// this will console.log the text after 4 seconds
setTimeout(() => {
console.log('almost...')
callback()
}, 4000)
}
asynchFunctionWithCallback(nextFunction)
// waits 4 seconds...
// => "almost..."
// => "I'm done
// :-)
```

- That being said, heavily nesting callbacks to handle sequences of tasks containing multiple asynchronous tasks can lead to callback hell. Using the `Promise` API provides a better API to avoid callback hell.
#### Promise Example: Fake Server

- In this example, we have a fake server that will return a successful or failed response at random after a 2 second wait:

- ```js
const serverRequestSimulation = () => {
return new Promise((resolve, reject) => {
// wait 2 seconds then run finished
setTimeout(() => {
finished()
}, 2000)
// after two seconds, randomly pick whether
// we succeeded or failed
const finished = () => {
const succeeded = Math.random() >= 0.5
// return back a success message
if (succeeded) {
resolve('you were successful')
// return back an error message
} else {
reject('you have utterly failed')
}
}
})
}
// success handler
const serverError = (err) => {
console.log('ERROR! HERE IS YOUR ERROR: ' + err)
}
// error handler
const serverSuccess = (data) => {
console.log('SUCCESS! HERE IS YOUR DATA: ' + data)
}
// running this function returns back an Promise object that
// is already running it's executor function
serverRequestSimulation()
// if the request was successful, run
// serverSuccess function
.then(serverSuccess)
// if the request was successful, run
// serverFailure function
.catch(serverError)
```

- If we want to be able to reuse promises, we should always wrap them in a function as above that returns a `new Promise`. This allows us to use `.then` and `.catch` to assign success and failure callbacks to the promise as seen above.

- We can see that our executor function takes in `resolve` and `reject` as arguments, and after completing the asynchronous task (waiting 2 seconds), randomly picks success or failure and uses `resolve` or `reject` to run the `serverSuccess` or `serverError` callbacks in either case.

Binary file not shown.
Binary file not shown.

0 comments on commit eb50d2c

Please sign in to comment.