Promise chainning
what is Promise chainning
A Promise-chain starts with new Promise
constructor and usethen/catch
as middle-party to pass the promise.result
which is formed like this:
1 | new Promise(function (resolve, reject) { |
why it’s created
We have a sequence of asynchronous tasks needed to be performed one after another In other words:When dealing with tasks that have sequencial order
for instance, loading scripts.
we can’t hold all tasks in one “then” handler
It’s known that When a handler returns a value, it becomes the result of that promise and in this chainning system
the promise holding a value peformed by the last handler become the input of next handler
analogy:
If you look at this in a broad way
you can see Promise keep a emerge-dive pattern like a joyful fish
The Promise-wrapper is the same just the inside-value(or result) changes each time emerge-dive
returning promise
A handler, used in .then(handler)
may create and return a promise.
In that case further handlers wait until it settles, and then get its result.
For instance:
1 | new Promise(function (resolve, reject) { |
[!NOTE]
In
then
handler if you return a value the value become a Promise for the nextthen
But returning a Promise seems more reasonable
So the output is the same as in the previous example: 1 → 2 → 4, but now with 1 second delay between alert
calls.
Here we go ! Useful example : loadscript
1 | loadScript("/article/promise-chaining/one.js") |
This code can be made bit shorter with arrow functions:
1 | loadScript("/article/promise-chaining/one.js") |
A special feature
This feature will allow us to integrate custom objects with promise chains without having to inherit from Promise
What does above mean ? To understand that there should be a clarification:
Actually in the promise-chain It’s not promise that continuely flow up and down (return and input)(that means what the handler returns is not Promise ) It’s another special object called “thenable” object
All the things we said above still valid because this “thenable” object has .then method and can be treated like Promise.So actually it gives us flexibilty!Because we can build like an architect on a object that has already implemented with basic Promise-function
1 | class Thenable { |
JavaScript checks the object returned by the .then
handler in line (*)
: if it has a callable method named then
, then it calls that method providing native functions resolve
, reject
as arguments (similar to an executor) and waits until one of them is called. In the example above resolve(2)
is called after 1 second (**)
. Then the result is passed further down the chain.
[!TIP]
A Thenable class should still define the
then
method inside
And what is passed into the constructor is actually thepromise.result
example associated with fetch
We’ll use the fetch method to load the information about the user from the remote server. It has a lot of optional parameters covered in separate chapters, but the basic syntax is quite simple:
1 | let promise = fetch(url); |
fetch(url) is saying :make a network request to the url
and returns a promise. The promise resolves with a response
object when the remote server responds with headers, but before the full response is downloaded.
[!NOTE]
You haven’t got the infomation now
To read the full response, we should call the method response.text()
: it returns a promise that resolves when the full text is downloaded from the remote server, with that text(information we require) as a result.
[!NOTE]
So response is a promise and response.text() is also a promise
The code below makes a request to user.json
and loads its text from the server:
1 | fetch("/article/promise-chaining/user.json") |
The response
object returned from fetch
also includes the method response.json()
that reads the remote data and parses it as JSON. In our case that’s even more convenient
1 | // same as above, but response.json() parses the remote content as JSON |
let’s see a more complex one :
1 | // Make a request for user.json |
this works but below is much better :
1 | fetch("/article/promise-chaining/user.json") |
[!CAUTION]
As a good practice, an asynchronous action should always return a promise.
That makes it possible to plan actions after it; even if we don’t plan to extend the chain now, we may need it later.
When should you consider about Promise
In the last chapter Promise we bury a seed in the last.This is the position it should get settled
So promise is actually used in a situation : 1.the task is gonna need some time 2. there are two possible consequences of the task3.mostly have to do with networking connection
let us see some egs
fetch("https://api.examplecom/data") .then((response) => response.json()) .then((data) => console.log(data)) .catch((error) => console.log(error));
Why do you need to bind functionc to events of img? Cause two callback must be called .And when you wanna use then/catch handler to link the function(LoadImage).It should be a produced (fully formed)promise in front of these handlers1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
fetch is a api that allows us to input a url as a door to get data and this api return a promise
(of course! 1.Getting is absolutely a task that requires some time2.two consequences : get it or miss it)
And during each level of the chain promise.result is passed as paramter for handler
2. ```js
function LoadImage(url) {
return new Promise((resolve, reject) => {
const img = new Image(); //create a img
img.src = url;
img.onload = () => resolve(img);
img.onerror = () => reject(new Error("fail loading"));
});
}
LoadImage("path/to/image.jpg")
.then((img) => document.body.appendChild(img))
.catch((error) => console.error(error));Custom Promise
Promise basically follow this pattern:1
2
3
4
5
6
7
8
9
10
11
12function doSomething() {
return new Promise((resolve, reject) => {
if (success) {
resolve(result);
} else {
reject(new Error("error"));
}
});
}
doSomething()
.then((result) => console.log(result))
.catch((error) => console.error(error));Ajax with XMLHttpRequest
1
2
3
4
5
6
7
8
9
10
11
12function ajax(url) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.onload = () => resolve(xhr.responseText);
xhr.onerror = () => reject(new Error("ajax failed"));
xhr.send();
});
}
ajax("https://")
.then((data) => console.log(data))
.catch((error) => console.log(error));