Easy Tutorial
❮ Js Tutorial Js Function Invocation ❯

JavaScript Promise

Before studying 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 to write complex asynchronous tasks more elegantly.

Since Promise is a new addition in ES6, some older browsers do not support it. Apple's Safari 10 and Windows' Edge 14 and above才开始 support ES6 features.

Here is the browser support situation for Promise:

Chrome 58 Edge 14 Firefox 54 Safari 10 Opera 55

Constructing a Promise

Now let's create a new Promise object:

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

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

Example

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

This program implements this functionality, but it is done using a "function waterfall." It is easy to imagine that in a complex program, a program implemented with a "function waterfall" is particularly cumbersome to maintain and handle exceptions, and it can make the indentation format very redundant.

Now let's implement the same functionality using Promise:

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 you don't need to fully understand it yet. I want to draw attention to the fact that Promise has transformed nested format code into sequential format code.

Using Promise

Below, we will dissect this Promise "timer" code to explain the use of Promise:

The Promise constructor has only one parameter, which is a function. This function is executed asynchronously immediately after construction, so we call it the starting function. The starting function contains two parameters, resolve and reject.

When the Promise is constructed, the starting function is executed asynchronously:

Example

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

This program will output Run directly.

resolve and reject are both functions. Calling resolve indicates 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 .then(), .catch(), and .finally() methods. The parameters of these three methods are all functions. .then() can add the function to the normal execution sequence of the current Promise, .catch() sets the exception handling sequence of the Promise, and .finally() is a sequence that will always execute at the end of the Promise execution. The function passed into .then() will be executed in order, and any exception will directly jump 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);
});

Execution result:

1111
2222
3333
An error

resolve() can pass a parameter to the next then to pass a value. The function in then can also return a value to pass to then. However, if the value returned in then is a Promise object, the next then will operate on this returned Promise. This can be seen from the timer example.

The reject() parameter generally passes an exception to the catch function for handling the exception.

But note the following two points:

Promise Function

The "timer" program above looks longer than the function waterfall, so we can make the core part of it look better by writing it 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 implement the program functionality with confidence:

Example

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

This function that returns a Promise object is called a Promise function. It is often used for developing libraries based on asynchronous operations.

FAQ

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

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

Q: Can other blocks besides the then block be used multiple times?

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

Q: How to interrupt a then block?

A: The then block will execute sequentially by default, and return cannot interrupt it. You can use throw to jump to catch to achieve interruption.

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

A: When you need to execute asynchronous operations sequentially multiple times. For example, if you want to asynchronously check the username first and then the password, Promise is suitable.

Q: Is Promise a method 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 continuing programming in the current then?

A: When you need to call another asynchronous task.

Asynchronous Function

Asynchronous functions (async function) are a specification of ECMAScript 2017 (ECMA-262) and are supported by almost all browsers except Internet Explorer.

In Promise, we wrote a Promise function:

Example

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

Then we output three lines of text 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();

Ha! Isn't this making asynchronous operations as easy as synchronous operations!

The answer is yes. Asynchronous functions can use the await directive, which must be followed by a Promise. The asynchronous function will pause here during the execution of this Promise until it finishes running, then continue.

The principle of asynchronous functions is exactly the same as the Promise native API mechanism, but it is more readable for programmers.

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

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 a 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

❮ Js Tutorial Js Function Invocation ❯