JavaScript Promise


Before learning the content of this chapter, you need to understand what asynchronous programming is, you can refer to: JavaScript asynchronous programming

Promise is a class provided by ECMAScript 6, whose purpose is to write complex asynchronous tasks more elegantly.

Since Promise is a new addition to ES6, some old browsers do not support it. Apple's Safari 10 and Windows' Edge 14 and above browsers have only begun to support ES6 features.

The following are the conditions supported by the Promise browser:

image.pngimage.pngimage.pngimage.pngimage.png
Chrome 58Edge 14Firefox 54Safari 10Opera 55

Construct Promise

Now we create a new Promise object:

new Promise(function (resolve, reject) {
    // Things to do...
});

By creating a new Promise object, it doesn't seem to see how it "writes complex asynchronous tasks more elegantly". The asynchronous tasks we encountered before were all one-time asynchronous. What if we need to call asynchronous functions multiple times? For example, if I want to output the string three times, the first time is 1 second apart, the second time is 4 seconds apart, and the third time is 3 seconds apart:

Example

setTimeout(function () {
     console.log("First");
     setTimeout(function () {
         console.log("Second");
         setTimeout(function () {
             console.log("Third");
         }, 3000);
     }, 4000);
}, 1000);

This program implements this function, but it is implemented with a "function waterfall". It is conceivable that, in a complex program, a program implemented with a "function waterfall" is a particularly cumbersome matter for maintenance or exception handling, and it will make the indentation format very cumbersome.

Now we use Promise to achieve the same function:

Example

new Promise(function (resolve, reject) {
     setTimeout(function () {
         console.log("First");
         resolve();
     }, 1000);
}).then(function () {
     return new Promise(function (resolve, reject) {
         setTimeout(function () {
             console.log("Second");
             resolve();
         }, 4000);
     });
}).then(function () {
     setTimeout(function () {
         console.log("Third");
     }, 3000);
});

This code is longer, so I don't need to fully understand it yet. What I want to notice is that Promise turns the nested format code into sequential format code.

Promise usage

Below we explain the use of Promise by analyzing this Promise "timer" code:

The Promise constructor has only one parameter, which is a function. This function will be run asynchronously directly after construction, so we call it the initial function. The start function contains two parameters resolve and reject.

When the Promise is constructed, the start function will be executed asynchronously:

Example

new Promise(function (resolve, reject) {
     console.log("Run");
});

This program will directly output Run .

Both resolve and reject are functions, in which resolve means everything is normal, and reject is called when an exception occurs:

Example

new Promise(function (resolve, reject) {
     var a = 0;
     var b = 1;
     if (b == 0) reject("Divide zero");
     else resolve(a / b);
}).then(function (value) {
     console.log("a / b = "+ value);
}).catch(function (err) {
     console.log(err);
}).finally(function () {
     console.log("End");
});

The execution result of this program is:

a / b = 0
End

The Promise class has three methods: .then(), catch() and .finally(). The parameters of these three methods are all a function. .then() can add the function in the parameter to the normal execution sequence of the current Promise. .catch() sets the Promise's exception handling sequence, and .finally() is the sequence that must be executed at the end of the Promise execution. The functions passed in .then() will be executed sequentially, and any exceptions will jump directly to the catch sequence:

Example

new Promise(function (resolve, reject) {
     console.log(1111);
     resolve(2222);
}).then(function (value) {
     console.log(value);
     return 3333;
}).then(function (value) {
     console.log(value);
     throw "An error";
}).catch(function (err) {
     console.log(err);
});

Results of the:

1111
2222
3333
An error

A parameter can be placed in resolve() to pass a value to the next then, and the function in then can also return a value and pass it to then. However, if what is returned in then is a Promise object, then the next then will be equivalent to operating on the returned Promise, as can be seen from the timer example just now.

In the reject() parameter, an exception is generally passed to the subsequent catch function to handle the exception.

But please note the following two points:

  • The scope of resolve and reject is only the initial function, excluding then and other sequences;

  • Resolve and reject can't stop the start function, don't forget to return.

Promise function

The above "timer" program looks longer than the function waterfall, so we can write its core part as a Promise function:

Example

function print(delay, message) {
     return new Promise(function (resolve, reject) {
         setTimeout(function () {
             console.log(message);
             resolve();
         }, delay);
     });
}

Then we can rest assured and boldly realize the program function:

Example

print(1000, "First").then(function () {
     return print(4000, "Second");
}).then(function () {
     print(3000, "Third");
});

This kind of function whose return value is a Promise object is called a Promise function, and it is often used to develop libraries based on asynchronous operations.

Answer frequently asked questions (FAQ)

Q: Can the sequence of then, catch and finally be reversed?

A: Yes, the effect is exactly the same. But it is not recommended to do so, it is best to write the program in the order of then-catch-finally.

Q: Except for the then block, can the other two blocks be used multiple times?

A: Yes, finally and then will be executed in order, but the catch block will only execute the first one, unless there is an exception in the catch block. So it is best to arrange only one catch and finally block.

Q: How to interrupt the then block?

A: The then block will be executed in the downward order by default. The return cannot be interrupted. You can jump to the catch to achieve interruption by throwing.

Q: When is it appropriate to use Promise instead of traditional callback function?

A: When you need to perform asynchronous operations multiple times in sequence, for example, if you want to detect the user name and password sequentially through the asynchronous method, you need to detect the user name asynchronously first, and then asynchronously detect the password, which is very suitable for Promise.

Q: Is Promise a way to convert asynchronous to synchronous?

A: Not at all. Promise is just a better programming style.

Q: When do we need to write another then instead of programming in the current then?

A: When you need to call an asynchronous task again.

Asynchronous function

The async function is a specification of the ECMAScript 2017 (ECMA-262) standard and is supported by almost all browsers except Internet Explorer.

We wrote a Promise function in Promise:

Example

function print(delay, message) {
     return new Promise(function (resolve, reject) {
         setTimeout(function () {
             console.log(message);
             resolve();
         }, delay);
     });
}

Then three lines of text were output with different time intervals:

Example

print(1000, "First").then(function () {
     return print(4000, "Second");
}).then(function () {
     print(3000, "Third");
});

We can make this code look better:

Example

async function asyncFunc() {
     await print(1000, "First");
     await print(4000, "Second");
     await print(3000, "Third");
}
asyncFunc();

what! Doesn't this make asynchronous operations as easy as synchronous operations!

The answer this time is yes. The await instruction can be used in the asynchronous function async function. The await instruction must be followed by a Promise. The asynchronous function will pause during the Promise operation and continue to run until the end of its operation.

In fact, the principle of asynchronous function is exactly the same as that of Promise native API, but it is easier for programmers to read.

The mechanism for handling exceptions will be implemented with a try-catch block:

Example

async function asyncFunc() {
     try {
         await new Promise(function (resolve, reject) {
             throw "Some error"; // or reject("Some error")
         });
     } catch (err) {
         console.log(err);
         // Will output Some error
     }
}
asyncFunc();

If the Promise has a normal return value, the await statement will also return it:

Example

async function asyncFunc() {
     let value = await new Promise(
         function (resolve, reject) {
             resolve("Return value");
         }
     );
     console.log(value);
}
asyncFunc();

The program will output:

Return value

More content

JavaScript Promise Object