SoFunction
Updated on 2025-04-09

Detailed explanation of the usage of Promise in ES6 learning tutorial

Preface

Promise has been used for so many years and has not been sorted out in a systematic way. Today I will sort out the relevant things about promises. If you are interested, you can take a look together. I try to analyze promises in a more understandable language

I'm going to explain the promise in two articles

One article to understand and use promises (this article) Another article to analyze the source code of promises from the perspective of promise usage functions (next article)

1. What is Promise

My understanding is: implementing a technique that allows us to write asynchronous code in a synchronous way. It is a kind of asynchronous solution.

It can queue multiple asynchronous operations so that they can be executed in sequence as we think.

So, are there any other asynchronous solutions before Promise. There are certainly some, and common ones include callback callback functions and events.

So what are the advantages of Promise? I think Promise has more powerful functions and can make our code clearer

  • Promise provides a unified API, making it easier for us to control asynchronous operations
  • Promise can avoid the layer-by-layer nesting of callback functions to make the code clearer. More readability and maintenance

2. Basic usage of Promise

First, let’s first understand some basic concepts of Promise

2.1. Promise status

There are 3 states in Promise, namely Pending (in progress), Resolved (completed, also known as Fulfilled) and Rejected (failed)
The state change can only be changed from Pending ------->Resolved, or from Pending ----------->Rejected. And once the state changes, it will not change again. The only result of asynchronous operation changes is the trigger state. The result is successful. The trigger status changes to Resolved. If the result fails or an error occurs in the middle, the trigger status changes to Rejected.

2.2 Promise structure

Promise is a constructor, so a Promise object can be instantiated through new Promise().

When new Promise(), a function is accepted as a parameter, and this function has two parameters, namely resolve and reject. resolve and reject are also two functions. They are provided by the JavaScript engine and do not need to be deployed by themselves.

Each instantiated promise instance has two methods: .then() and .catch(). And the call of these two methods supports chain operation

OK, after understanding the concept, let’s take a look at the basic usage of Promise

First, how to instantiate a promise object

const promise = new Promise((resolve, reject) => {
	setTimeout(() => {
		if (/* success */) {
			resolve(res)
		} else {
			reject(err)
		}
	}, 100)
})

In the above figure, a promise instance is instantiated through new Promise(). Note: The function in the new Promise() method is an immediate execution function, that is, it will be executed the moment new Promise() is. The code inside the function is synchronous code.

resolve and reject are used to return the results of an asynchronous operation. When using resolve(), the promise state will be returned by Pending—>Resolved, and the correct result of the asynchronous is returned. When reject() is used, the promise state is Pending---->Rejected and the error message is returned

Let's see how this object receives the returned results

((res) => {
	(res)
}).catch((err) => {
	(err)
})

In the figure above, the callback function of .then and the callback function of .catch are respectively used to receive the correct information returned by resolve() and the error information returned by reject.

Let's take a look at .then() and .catch() in detail below

.then() function

Then() function is a method of the Promise instance. Its function is to add a callback function when the state changes to the Promise instance.
It has the following characteristics

  1. Then() is added to the Promise prototype. That is, (), so all Promise instances exist. Then() method
  2. .then() can perform chain operations, that is, ().then().then(), and then the callback function will be called in order
  3. There are two parameters in the .then() function, which are generally functions. Among them, the first function will be executed only when the state becomes Resolved (we will collectively call it .then's resolve callback in the following text), and the parameters are the value when the Promise object resolve(res). The second function is executed only when the state becomes Rejected (we will collectively call it .then's reject callback). Later we will talk about the situations in which state will become Rejected
  4. Promise will have value penetration. When the two parameters of then() are not functions, they will penetrate into the next then(). If the next then() parameter is not a function, it will continue to penetrate downward.
  5. As we said above, when the resolve() method of the Promise instance is executed, the status of the instance will be changed to Resolved, so the .then resolve callback will be triggered when the current Promise instance resolve() is resolved().

Next, we will focus on analyzing the 2nd, 3rd, 4th, and 5th

function getData(url) {
 return new Promise((resolve, reject) => {
 setTimeout(() => {
  if (url) {
  resolve({
   code: 200,
   message: 'ok',
   data: 123
  })
  } else {
  reject(new Error('Missing url'))
  }
 }, 100)
 })
}
getData('').then((res) => {
 ('The first callback')
 (res)
}).then((res) => {
 ('Second callback')
 (res)
}).then((res) => {
 ('Third callback')
 (res)
})
// The first callback// { code: 200, message: 'ok', data: 123 }
// The second callback// undefined
// The third callback// undefined

It can be seen that first, when getData() resolve() is executed, the .then resolve callback function is called in sequence, but only the parameters of the first then() resolve callback function have values, while the other two are undefined. Why is this? Let's look at another code

getData('').then((res) => {
 ('The first callback')
 (res)
 return ()
}).then((res) => {
 (res)
})
// The first callback// { code: 200, message: 'ok', data: 123 }
// undefined

Looking at this code, we can find that when the previous resolve callback returns a(), the result we get when we don't return anything is the same. So can we understand that the resolve callback function of each .then() method will return a () by default after execution. That's right, I'll tell you, yes.

As for what () gets, I will tell you first, what he gets is a Promise instance of the resolve state. We will talk about this later.

At this time, we can summarize: starting from the second .then(), the one calling the resolve callback function of this .then is the Promise instance returned by the previous .then resolve callback. The parameter of the .then callback function is the value of the Promise instance resolved returned by the previous .then callback function. Let's check a code below

getData('').then((res) => {
 ('The first callback')
 (res)
 return new Promise((resolve, reject) => {
 resolve('123')
 })
}).then((res) => {
 (res)
})

// The first callback// { code: 200, message: 'ok', data: 123 }
// 123

Summarize:

  1. Each .then resolve callback will return a Promise object that returns a Resolved state by default.
  2. When you receive a new Promise instance returned, the default returned Promise instance will be overwritten
  3. The value of the returned Promise instance resolve() will be returned as the parameter of the next .then resolve callback.

Let's take a look at what happens if the then() parameter is not a function. Let's look at a piece of code

var getData = function() {
 return new Promise(function(resolve, reject) {
  resolve(123)
 });
};
getData().
then(345)
.catch(err => {
 ('Catched an error')
 (err)
}).then((res) => {
 ('I'm the second then')
 (res)
})
// OutputI'm the secondthen
123

As shown in the figure above, we can see that when our first then resolve callback is not a function, but a number 345, resolve(123) penetrates into the second then, triggering the execution of the second then resolve callback, and giving the return value of resolve to the second then resolve callback. This phenomenon is called value penetration.

var getData = function() {
 return new Promise(function(resolve, reject) {
  reject(new Error(123))
 });
};
getData().
then(345)
.catch(678)
.then((res) => {
 ('I'm the second then')
 (res)
}).catch(err => {
 ('I'm the second catch')
 (err)
})
// OutputI'm the secondcatch
Error: 123

You can see that when an error is reported, value penetration also occurs.

At this point, the first parameter of .then() and then() are finished, and the second parameter, we put it in the .catch() method, together

.catch() function

catch() is also a method () mounted under the Promise object prototype. Like then(), all Promise objects also have catch methods. Its function is to specify the callback function when an error occurs, that is, to capture the errors occurring in asynchronous operations
What are its characteristics? Let's summarize it first, and then verify it one by one.

  1. .catch() will specify a parameter as a callback when an error occurs, so the parameter of catch((err) => {}) will be triggered when the Promise state changes to Rejected.
  2. The second parameter of .then(null, (err) => {}) is also triggered when the Promise state changes to Rejected. Therefore, the reject callback functions of .catch() and .then() are essentially the same, but the writing method is different. But we generally prefer to use .catch() instead of .then's reject callback. The reason will be discussed later
  3. The code throws an error and the execution of reject() function will change the state of the Promise object to Rejected, so both cases will trigger the callback execution of catch() or the reject callback execution of then(). Therefore, the essence of reject() is actually to throw an error
  4. The callback function of .catch() and the reject callback of .then will return a Promise object with the status Resolved by default during execution (that is, return ())
  5. Like .catch() and .then(), it can also write multiple items and also supports chain operations. The reason is the third point above
  6. Once the thrown error is caught by the catch, it will not spread outward again. Only when the error is thrown out again will it continue to be captured by the subsequent catch. Therefore, the error has bubbled nature and will spread out step by step until it is caught by the catch.

1. Let’s look at the first point:

var getData = function() {
 return new Promise(function(resolve, reject) {
  reject(123)
 });
};
getData().then((res) => {
 ('success')
}).catch((err) => {
 ('Catch error')
 (err)
})

//Catch an error// 123

There is no doubt that reject(123) throws an error, catch's callback catches the error, and outputs

2. Let’s look at the second point:

var getData = function() {
 return new Promise(function(resolve, reject) {
  reject(123)
 });
};
getData().then((res) => {
 ('success')
}, (err) => {
	('Catched an error')
	(err)
})

//Catch an error// 123

It can also be seen from the code that the above two methods are the same.

Now, let me talk about why it is recommended to use catch() instead of the reject callback of then(). Look at the following code

var getData = function() {
 return new Promise(function(resolve, reject) {
  resolve(123)
 });
};
getData().then((res) => {
 ('success')
 return new Promise((resolve, reject) => {
  reject(new Error('123'))
 })
}, (err) => {
	('Catched an error')
	(err)
})

// success

At this time, only the output is successful, and the error thrown in the resolve callback is not caught

Look at the following code

var getData = function() {
 return new Promise(function(resolve, reject) {
  resolve(123)
 });
};
getData().then((res) => {
 ('success')
 return new Promise((resolve, reject) => {
  reject(new Error('123'))
 })
}).catch((err) => {
	('Catched an error')
	(err)
})

success
Catch an error
Error: 123

See, the same error, but using catch() can be caught, but using then() reject callback cannot be caught.

Conclusion: catch() can catch errors anywhere (referring to the Promise) by putting them at the bottom of the operation chain. The reject callback of then() can only catch the error before the execution of this .then(). The errors in the resolve callback currently executed cannot be caught, and the errors thrown by the code executed later cannot be caught. And the code level of .catch is clearer

var getData = function() {
 return new Promise(function(resolve, reject) {
  resolve(123)
 });
};
getData().then((res) => {
 ('success')
 return new Promise((resolve, reject) => {
  reject(new Error('123'))
 })
}, (err) => {
 ('The first error catch')
}).then((res) => {
 ('The second resolve callback')
}, err => {
 ('The second error catch')
})

success
The second error capture

As shown in the above figure, the error thrown in the first then resolve callback is caught by the reject callback in the second then

Therefore, the conclusion: In general, do not use the second parameter of then, but use the .catch() method as much as possible to catch the errors.

3. Let's look at the third point below

var getData = function() {
 return new Promise(function(resolve, reject) {
  resolve(x)
 });
};
getData().then((res) => {
 ('success')
}).catch(err => {
 ('Catched an error')
 (err)
 throw new Error('I threw an error')
}).catch(err => {
 ('I caught a mistake too')
 (err)
})

Catch an error
ReferenceError: x is not defined
I also caught an error
Error: I threw an error

As can be seen from the above code, an error will not be thrown only after reject() is executed. x is not defined, and the system will automatically throw an error, and throw new Error is that we manually throw an error by ourselves. These will change the state of the Promise object to Rejected, thereby triggering the catch.
At the same time, we can also see the sixth point we wrote above. The error will spread out in a bubble manner. After being caught, it will no longer be propagated. Until an error is thrown again. In the above code, after the first error is caught by the first catch, the second catch will not leave again, but because another error was thrown in the first catch, the execution of the second catch is caused.

4. Let’s look at the fourth point (the callback function of catch() will also return a Promise instance with the status Resolved)
In fact, we can also see this from the above picture. The first catch() callback originally wanted to return a Promise with a Resolved state, but because of an error throwing, the state of the Promise instance changed to Rejected and returned, and became a Rejected change, triggering the callback execution of the second catch.

Let's look at the following code and verify it again

var getData = function() {
 return new Promise(function(resolve, reject) {
  resolve(x)
 });
};
getData().then((res) => {
 ('success')
}).catch(err => {
 ('Catched an error')
 (err)
}).then((res) => {
 ('I'm the second then')
})

Catch an error
ReferenceError: x is not defined
I'm the secondthen

As can be seen from the above code, the then behind it is still executed after the callback is executed. Why is it because the callback of the catch is executed and a Promise instance with the Resolved state is returned by default (return ())

The fifth point and the sixth point have been verified. No more to say.

Implement simple axios

We use axios more commonly, and everyone should have discovered that the usage of axios seems to be the same as Promise.

axios({
 url:'',
 method: 'post',
 data: {}
}).then((res) => {
 (res)
}).catch((err) => {
 (err)
})

That's right. axios is a Promise instance. It is an XMLHttpRequest encapsulated with Promise
Let's implement a simple axios

function MyAxios(option) {
 return new Promise((resolve, reject) => {
  const http = new XMLHttpRequest()
  (, );
   = "json";
  ("Accept", "application/json");
   = myHandler;
  ();

  function myHandler() {
   if ( !== 4) {
    return;
   }
   if ( === 200) {
    resolve();
   } else {
    reject(new Error());
   }
  }
 })
}
MyAxios({
 url:'',
 method: 'post'
}).then((res) => {
 (res)
}).catch((err) => {
 (err)
})

, and the difference between the two

1、

() can execute multiple Promise() in parallel and return a new Promise instance

var p = ([p1, p2, p3]); // p1, p2, p3 are 3 Promise instances

The parameters of () are not necessarily arrays, as long as the data with the Iterator interface is OK (Iterator is a traverser, I won’t introduce it too much here. If you are interested, you can go to the official website to check it out). However, the member returned after the parameter traversal must be a Promise object (such as above, p1, p2, and p3 must be Promise objects. If not, (p1) will be called first to convert it into a Promise instance)

So, how is the status of the Promise instance returned by () defined.

  • Only when the states of each member of the parameter (p1, p2, p3) become Resolved, the state of p will become Resolved.
  • Among each member of the parameter, any state becomes Rejected, and the state of p will immediately become Rejected.
function getData (data) {
 return new Promise((resolve, reject) => {
  setTimeout(() => {
   if (data === 6) {
    reject(new Error('The request error occurred'))
   } else {
    resolve(data)
   }
  }, data * 500)
 })
}
const promises = [1,3,5,7].map((item) => {
 return getData(item)
})
(promises)
.then((res) => {
 ('Request succeeded')
 (res)
}).catch((err) => {
 ('Request failed')
 (err)
})

// Output after 3.5sRequest succeeded
[ 1, 3, 5, 7 ]

As shown in the figure above, the state of the last member (the promise instance returned by 7 in the figure above) is changed to Resolved after 3.5s, so the resolve callback of .then() is executed after 3.5s.

function getData (data) {
 return new Promise((resolve, reject) => {
  setTimeout(() => {
   if (data === 6) {
    reject(new Error('The request error occurred'))
   } else {
    resolve(data)
   }
  }, data * 500)
 })
}
const promises = [2,4,6,8].map((item) => {
 return getData(item)
})
(promises)
.then((res) => {
 ('Request succeeded')
 (res)
}).catch((err) => {
 ('Request failed')
 (err)
})

// Output after 3sRequest failed
Error: An error occurred in the request

As can be seen from the above figure, when we use 2, 4, 6, and 8 to get the promise member, an error occurred at the time of the third s. At this time, the status of the () returns the Promise instance immediately changes to Rejected, and the callback of catch() is immediately triggered. Therefore, the output error

2、()

The role of () and () are the same, both process multiple Promise instances concurrently and return a new instance.
The difference is that the status of the new Promise instance returned by the two is different.

Yes All Promise sub-member states become Resolved, and the new Promise instance state will become Resolved. If any child member state becomes Rejected in the middle, the state of the new Promise instance will immediately become Rejected.

Yes As long as any state of a child member changes (whether it becomes Resolved or Rejected), the state of the returned new Promise instance will change immediately, and the changing state is the state that the child member changes.

function getData (data) {
 return new Promise((resolve, reject) => {
  setTimeout(() => {
   if (data === 6) {
    reject(new Error('The request error occurred'))
   } else {
    resolve(data)
   }
  }, data * 500)
 })
}
const promises = [2,4,6,8].map((item) => {
 return getData(item)
})
(promises)
.then((res) => {
 ('Request succeeded')
 (res)
}).catch((err) => {
 ('Request failed')
 (err)
})

// Output after 1sRequest succeeded
2

As can be seen from the figure above, the state of the first child member changed to Resolved after 1s, and the status of the returned new Promise instance is immediately changed to Resolved, so the resolve callback of .then() after 1s is executed. The output request was successful.

Finally, let’s talk about the () I used earlier

and

()

As we mentioned earlier, you can return a Promise object with a status of Resolved. Yes, it is equivalent to

new Promise((resolve, reject) => {
 resolve()
})

When () has parameters, a Promise object will be returned, and the parameter will be returned as the parameter of the then() resolve callback (except when the parameter is the thenable object, it will be later). There are mainly the following situations

1. When the parameter is a Promise object

This parameter will be returned directly without any changes

2. When the parameter is the thenable object, (then() method exists), as follows

let obj= {
 then: function(resolve, reject) {
 resolve('I'm the thenable object');
 }
};

At this time, (obj) will return a Promise object and call obj's then() method. Alas, note here that this .then() is not the .then() of the new Promise object. Obj's then() will be executed immediately, which does not mean that the then() callback of the new Promise object will also be executed. Do you remember that the condition for the execution of the then() callback of the Promise object we mentioned earlier is that the state of the Promise object will only be executed if it changes.

let obj= {
 then: function(resolve, reject) {
  (123)
 }
};
 
let p1 = (obj);
(function(value) {
 ('success')
 (value); // 42
});
// Output123

As can be seen from the above figure, () was executed immediately, but the then callback of Promise was not executed

3. Parameters are not objects, or objects without .then methods

A Promise instance will be returned and the parameter will be returned as a parameter of the resolve callback of .then()

For example, (‘123’) is equivalent to

new Promise((resolve, reject) => {
 resolve('123')
})

4. Without parameters, that is, (), which is what we said earlier.

A Promise object with a Resolved state is returned, but the resolve callback of .then() has no parameters.

new Promise((resolve, reject) => {
 resolve()
}).then((res) => {
	(res)
})
// Outputundefined

()

() also returns a Promise object, but the state of this object is Rejected
As for the usage of parameters, the only difference is that there is no such parameter, which means that when there are parameters, the parameter will be returned as a callback parameter of catch() in any case. In other words, there is no difference between the previous 1, 2, and 3 types. You can try it, I won’t explain it too much.

Summarize

This is the article about the detailed explanation of the usage of Promise in ES6 learning tutorial. For more information about the usage of Promise in ES6, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!