Synchronous: Refers to real-time communication where each party response to a message instantly.
Asynchronous: Refers to the method of exchanging messages between parties where each party receive and process the message when it's convenient to do so, rather than doing so immediately.
Example of Asynchronous
Fetching a file from the network
Accessing a database and returning data from it
Accessing a video stream from a web cam
Broadcasting the display to a VR headset
Why async ?
let response = fetch('myImage.png'); // fetch is asynchronous
let blob = response.blob();
// display your image blob in the UI somehow
We don't know how long it takes to download the image so the second line will throw an error since the response is not yet available
The second line will need to wait until the response is returned
How to implement async ?
Async callbacks
Promises
Async callbacks
Async callbacks are functions that are specified as arguments --> The callback function is not executed immediately
The containing function is responsible for executing the callback function when the time comes.
function loadAsset(url, type, callback) {
let xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.responseType = type;
xhr.onload = function() {
callback(xhr.response);
};
xhr.send();
}
function displayImage(blob) {
let objectURL = URL.createObjectURL(blob);
let image = document.createElement('img');
image.src = objectURL;
document.body.appendChild(image);
}
loadAsset('coffee.jpg', 'blob', displayImage);
- The containing function is `loadAsset()`. It uses the `XMLHttpRequest` to fetch the resource at the given URL, then pass the response to the callback to do something with
- The callback function is `displayImage()`. It is waiting on the XHR call to finish downloading the resource (using the onload event handler), then creates an image to display the URL in, appending it to the document's body
#### Problem with callback
```javascript
chooseToppings(function(toppings) {
placeOrder(toppings, function(order) {
collectOrder(order, function(pizza) {
eatPizza(pizza);
}, failureCallback);
}, failureCallback);
}, failureCallback);
```
- This is messy and hard to read (often referred to as "callback hell")
- It requires the failureCallback() to be called multiple times (once for each nested function), with other issues besides.
- Use Promises will be much nicer
## Improvements with promises
### What are promises?
- An object that represents an intermediate state of an operation — in effect, a result of some kind will be returned at some point in the future.
- There is no guarantee of exactly when the operation will complete and the result will be returned.
- But there is a guarantee that when the result is available, the code will be executed in order to do something else with a successful result, OR if the promise fails, the code will be executed to gracefully handle a failure case.
### Recap
- When `a promise is created`, it is neither in a success or failure state. It is said to be **pending**.
- When `a promise returns`, it is said to be **resolved**.
- A `successfully resolved promise` is said to be **fulfilled**. It returns **a value**, which can be accessed by chaining a `.then()` block onto the end of the promise chain. The callback function inside the .then() block will contain the promise's return value.
- An `unsuccessful resolved promise` is said to be **rejected**. It returns **a reason** - an **error message** stating why the promise was rejected. This reason can be accessed by chaining a `.catch()` block onto the end of the promise chain.
### Example with fetch Promises
- We can chain multiple async operations together using multiple `.then()` operations.
- All errors are handled by a single `.catch()` block at the end of the block
```javascript
fetch('coffee.jpg')
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
} else {
return response.blob();
}
})
.then(myBlob => {
let objectURL = URL.createObjectURL(myBlob);
let image = document.createElement('img');
image.src = objectURL;
document.body.appendChild(image);
})
.catch(e => {
console.log('There has been a problem with your fetch operation: ' + e.message);
});
```
- **Fetch promises** do not fail on 404 or 500 errors — only on something catastrophic like a network failure. Instead, they succeed, but with the response.ok property set to `false`. To produce an error on a 404, for example, we need to check the value of response.ok, and if false, throw an error, only returning the blob if it is true.
- Each call to `.then()` creates a **new promise**. The value returned by a fulfilled promise becomes the parameter passed to the next .then() block's callback function.
### Multiple promises fulfilling
```javascript
// Define function to fetch a file and return it in a usable form
function fetchAndDecode(url, type) {
return fetch(url)
.then(response => {
if(!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
} else {
if(type === 'blob') {
return response.blob();
} else if(type === 'text') {
return response.text();
}
}
})
.catch(e => {
console.log(`There has been a problem with your fetch operation for resource "${url}": ` + e.message);
});
}
// Call the fetchAndDecode() method to fetch the images and the text, and store their promises in variables
let coffee = fetchAndDecode('coffee.jpg', 'blob');
let tea = fetchAndDecode('tea.jpg', 'blob');
let description = fetchAndDecode('description.txt', 'text');
// Use Promise.all() to run code only when all three function calls have resolved
Promise.all([coffee, tea, description])
.then(values => {
console.log(values);
// Store each value returned from the promises in separate variables; create object URLs from the blobs
let objectURL1 = URL.createObjectURL(values[0]);
let objectURL2 = URL.createObjectURL(values[1]);
let descText = values[2];
// Display the images in <img> elements
let image1 = document.createElement('img');
let image2 = document.createElement('img');
image1.src = objectURL1;
image2.src = objectURL2;
document.body.appendChild(image1);
document.body.appendChild(image2);
// Display the text in a paragraph
let para = document.createElement('p');
para.textContent = descText;
document.body.appendChild(para);
});
```
- The `.then()` callback function will only run when `all three promises resolve`; when that happens, it will be passed an array containing the results from the individual promises (i.e. the decoded response bodies), kind of like [coffee-results, tea-results, description-results].
## Rewriting promise code with async/await
- The `async` keyword can be put in front of a function declaration to turn it into an async function --> Invoking the function will return a promise.
```javascript
let hello = async function() { return "Hello" };
hello().then((value) => console.log(value))
```
- The `await` keyword can be put in front of any async promise-based function to pause the code on that line until the promise fulfills, then return the resulting value.
```javascript
async function hello() {
return greeting = await Promise.resolve("Hello");
};
hello().then(alert);
```
#### Example
```javascript
async function myFetch() {
let response = await fetch('coffee.jpg');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
let myBlob = await response.blob();
let objectURL = URL.createObjectURL(myBlob);
let image = document.createElement('img');
image.src = objectURL;
document.body.appendChild(image);
}
myFetch()
.catch(e => {
console.log('There has been a problem with your fetch operation: ' + e.message);
});
```
OR
```javascript
async function myFetch() {
let response = await fetch('coffee.jpg');
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.blob();
}
myFetch().then((blob) => {
let objectURL = URL.createObjectURL(blob);
let image = document.createElement('img');
image.src = objectURL;
document.body.appendChild(image);
}).catch(e => console.log(e));
```
Synchronous & Asynchronous
Synchronous vs Asynchronous
Example of Asynchronous
Why async ?
second line
will throw an error since the response is not yet availablesecond line
will need to wait until the response is returnedHow to implement async ?
Async callbacks
The containing function is responsible for executing the callback function when the time comes.
function displayImage(blob) { let objectURL = URL.createObjectURL(blob);
let image = document.createElement('img'); image.src = objectURL; document.body.appendChild(image); }
loadAsset('coffee.jpg', 'blob', displayImage);