Directory:

  • 1. An overview of the
  • 2. Basic usage
    • 2.1 create a Promise
    • 2.2 then method
    • 2.3 catch method
    • 2.4 Other ways to create Promise objects
  • 3. Advanced usage
    • 3.1 Flatten Promise
    • 3.2 Promise. All methods
    • 3.3 Promise. Race method
  • Use cases
  • 5. To summarize
  • 6. Reference links

1. An overview of the

You’ve all heard of Node’s famous callback hell. Because operations in Node are performed asynchronously by default, the caller needs to pass in a callback function to process the operation when it finishes. As the level of callbacks increases, the code becomes harder to write, understand, and read.

Promise is a new asynchronous programming approach in ES6 that addresses the issue of how callbacks work and offers more possibilities. Even before ES6, there were several ways to implement promises:

  • Q
  • bludbird
  • RSVP

All of the above Promise libraries follow the Promise/A+ specification. ES6 also uses the specification, so the apis for these implementations are similar and can be learned from each other.

A Promise represents a placeholder for a calculation result or a network request. The results are not available because the current calculation or network request has not been completed.

The Promise object has three states: pending, fullfilled, and rejected.

  • pending— The mission is still ongoing.
  • resolved— Mission accomplished.
  • reject— Task error.

A Promise object is initially in a pending state, and only one of the following state transitions can occur during its lifetime:

  • Task complete, status bypendingconvertresolved.
  • Task error returned, status bypendingconvertrejected.

Once the state transition of a Promise object occurs, it cannot be changed again. Maybe that’s what a Promise means.

2. Basic usage

2.1 createPromise

Javascript provides a Promise constructor to create Promise objects. The format is as follows:

let p = new Promise(executor(resolve, reject));
Copy the code

Executor in this code is a user-defined function that implements a specific asynchronous operation flow. This function has two arguments, resolve and reject, which are provided by the Javascript engine and do not require user implementation. In the Executor function, if the asynchronous operation succeeds, the resolve call converts the Promise state to Resolved, and the resolve function takes the result data as an argument. If the asynchronous operation fails, a reject call converts the Promise state to Rejected, with the reject function taking the specific error object as an argument.

2.2 thenmethods

Once the Promise object is created, we need to call the THEN (SUCC_handler, fail_handler) method to specify successful and/or failed callback handling. Such as:

let p = new Promise(function(resolve, reject) {
    resolve("finished");
});

p.then(function(data) { console.log(data); // output finished},function (err) {
    console.log("oh no, ", err.message);
});
Copy the code

In the code above, we created a Promise object and converted the state to Resolved by calling resolve in the Executor function.

The success callback specified by then is called, printing FINISHED.

let p = new Promise(function(resolve, reject) {
    reject(new Error("something be wrong"));
});

p.then(function (data) {
    console.log(data);
}, function (err) {
    console.log("oh no, ", err); // output oh no, something be wrong});Copy the code

In the code above, calling reject in the executor function converts the Promise object state to Rejected.

Then the specified failure callback is called, printing oh no, something be wrong.

This is the basic way to write asynchronous processing using promises. However, there are a few caveats:

(1) The then method can pass in only successful or failed callbacks.

(2) Executor functions are executed immediately, while successful or failed callbacks are executed at the end of the current EventLoop. The following code verifies this:

let p = new Promise(function(resolve, reject) {
    console.log("promise constructor");
    resolve("finished");
});

p.then(function (data) {
    console.log(data);
});

console.log("end");
Copy the code

The output is:

promise constructor
end
finished
Copy the code

(3) The then method returns a new Promise object, so it can be chain-called:

let p = new Promise(function(resolve) {
    resolve(5);
});

p.then(function (data) {
    return data * 2;
})
 .then(function(data) { console.log(data); });Copy the code

(4) The then method of a Promise object can be called multiple times, and can be called repeatedly (unlike events, callbacks to the same event can only be called once). .

let p = new Promise(function(resolve) {
    resolve("repeat");
});

p.then(function (data) {
    console.log(data);
});

p.then(function (data) {
    console.log(data);
});

p.then(function (data) {
    console.log(data);
});
Copy the code

Output:

repeat
repeat
repeat
Copy the code

2.3 catchmethods

From the previous introduction, we know that error handling can be specified by the THEN method. But ES6 provides a more useful method, catch. Catch (handler) is the same as THEN (null, handler).

let p = new Promise(function(resolve, reject) {
    reject(new Error("something be wrong"));
});

p.catch(function (err) {
    console.log("oh no, ", err.message); // output oh no, something be wrong});Copy the code

It is generally not recommended to specify error handling in the then method, but rather to add a catch method at the end of the call chain to handle errors that occurred in the previous step.

Pay attention to the following points when using:

  • The then method specifies two handlers that will not be called if the success handler throws an exception.

  • An unhandled exception in a Promise does not terminate the current execution flow, that is, the Promise will “swallow the exception” **.

let p = new Promise(function (resolve, reject) {
    throw new Error("something be wrong");
});

p.then(function (data) {
    console.log(data);
});

console.log("end"); // The program ends normally, the output endCopy the code

2.4 Other CreationPromiseObject mode

In addition to the Promise constructor, ES6 provides two easy-to-use ways to create Promise objects, promise.resolve and promise.reject.

Promise.resolve

Resolve creates an Resolved Promise object as the name suggests:

let p = Promise.resolve("hello");

p.then(function(data) { console.log(data); // print hello});Copy the code

The parameters to promise.resolve are of the following types:

(1) The argument is a Promise object, so return it directly.

(2) The argument is a Thenable object, which has the then function. Resolve converts the object to a Promise object and executes its then function immediately.

let thenable = {
    then: function (resolve, reject) {
        resolve(25);
    };
};

let p = Promise.resolve(thenable);

p.then(function(data) { console.log(data); // output 25});Copy the code

(3) Other parameters (no parameter is equivalent to having a undefined parameter), create an Resolved Promise object and pass the parameters as the operation result to subsequent callback processing.

Promise.reject

Reject Creates a Promise object in the Rejected state, regardless of the parameter type.

3. Advanced usage

3.1 Flatten Promise

The success callback of the then method can return a new Promise object, in which case the old Promise object will be frozen and its state will depend on the state of the new Promise object.

let p1 = new Promise(function (resolve) {
    setTimeout(function () {
        resolve("promise1");
    }, 3000);
});

let p2 = new Promise(function (resolve) {
    resolve("promise2");
});

p2.then(function (data) {
    return p1;  // (A)
})
  .then(function(data) { // (B) console.log(data); // output promise2});Copy the code

We return another Promise object directly in line (A). The subsequent then method execution depends on the state of the object, so promisE1 is output after 3s, not promisE2.

3.2 Promise. All methods

Many times, we want to wait for multiple asynchronous operations to complete before doing some processing. If you use the callback method, there will be the aforementioned callback hell. Such as:

let fs = require("fs");

fs.readFile("file1"."utf8".function (data1, err1) {
    if(err1 ! = nil) { console.log(err1);return;
    }

    fs.readFile("file2"."utf8".function (data2, err2) {
        if(err2 ! = nil) { console.log(err2);return;
        }

        fs.readFile("file3"."utf8".function (data3, err3) {
            if(err3 ! = nil) { console.log(err3);return;
            }

            console.log(data1);
            console.log(data2);
            console.log(data3);
        });
    });
});
Copy the code

Suppose the contents of files file1, file2, and file3 are “in file1”, “in file2”, and “in file3”, respectively. So the output looks like this:

in file1
in file2
in file3
Copy the code

In this case, promise.all comes in handy. Promise.all takes an Iterable (that is, an Iterable in ES6), and each element is converted to a Promise by calling Promise.resolve. The promise. all method returns a new Promise object. This object will be switched to Resolved only when the states of all Promise objects become Resolved and the parameters are an array of the results of each Promise. As soon as an object’s state changes to Rejected, the new object’s state changes to Rejected. Using promise.all we can elegantly implement the above:

let fs = require("fs");

let promise1 = new Promise(function (resolve, reject) {
    fs.readFile("file1"."utf8".function (err, data) {
        if(err ! = null) { reject(err); }else{ resolve(data); }}); });let promise2 = new Promise(function (resolve, reject) {
    fs.readFile("file2"."utf8".function (err, data) {
        if(err ! = null) { reject(err); }else{ resolve(data); }}); });let promise3 = new Promise(function (resolve, reject) {
    fs.readFile("file3"."utf8".function (err, data) {
        if(err ! = null) { reject(err); }else{ resolve(data); }}); });let p = Promise.all([promise1, promise2, promise3]);
p.then(function (datas) {
    console.log(datas);
})
 .catch(function (err) {
    console.log(err);
});
Copy the code

The output is as follows:

['in file1'.'in file2'.'in file3']
Copy the code

The second code can be further simplified as:

let fs = require("fs");

let myReadFile = function (filename) {
    return new Promise(function (resolve, reject) {
        fs.readFile(filename, "utf8".function (err, data) {
            if(err ! = null) { reject(err); }else{ resolve(data); }}); }); }let promise1 = myReadFile("file1");
let promise2 = myReadFile("file2");
let promise3 = myReadFile("file3");

let p = Promise.all([promise1, promise2, promise3]);
p.then(function (datas) {
    console.log(datas);
})
 .catch(function (err) {
    console.log(err);
});
Copy the code

3.3 Promise. Race method

Promise.race, like promise.all, takes an iterable as an argument and returns a new Promise object. The difference is that whenever the state of one of the Promise objects changes, the state of the new object changes. That is, whichever operation is fast, uses the result (or error). With this feature, we can implement timeout handling:

let p1 = new Promise(function (resolve, reject) {
    setTimeout(function () {
        reject(new Error("time out"));
    }, 1000);
});

let p2 = new Promise(function(resolve, reject) {// Simulate time-consuming operationssetTimeout(function () {
        resolve("get result");
    }, 2000);
});

let p = Promise.race([p1, p2]);

p.then(function (data) {
    console.log(data);
})
 .catch(function (err) {
    console.log(err);
});
Copy the code

After 1s, p1 changes to Rejected and P2 changes to Resolved after 2s. So after 1s, when THE P1 state changes to Rejected, the state of P then changes to Rejected. Thus, the output is:

time out
Copy the code

If you change the delay of object P2 to 0.5 seconds, then p will be followed by the transition to Resolved when p2 state changes after 0.5 seconds. Thus the output is:

get result
Copy the code

Use cases

As mentioned earlier, the then method returns a new Promise object. So the then method can be called chaining, with the return value of the previous successful callback as an argument to the next successful callback. Such as:

let p = new Promise(function (resolve, reject) {
    resolve(25);
});

p.then(function (num) { // (A)
    return num + 1;
})
 .then(function (num) { // (B)
    return num * 2;
})
 .then(function (num) { // (C)
    console.log(num);
});
Copy the code

When p becomes Resolved, the result is 25. The function is called first at line (A), with num having the value 25 and the return value 26. 26 is also used as an argument to the function at line (B), which returns 52. 52 is printed as an argument to the function at line (C).

Here is an example of combining AJAX.

let getJSON = function (url) {
    return new Promise(function (resolve, reject) {
        let xhr = new XMLHttpRequest();
        xhr.open('GET', url);
        xhr.onreadystatechange = function () {
            if(xhr.readyState ! = = 4) {return;
            }

            if (xhr.status === 200) {
                resolve(xhr.response);
            } else {
                reject(new Error(xhr.statusText));
            }
        }
        xhr.send();
    });
}

getJSON("http://api.icndb.com/jokes/random")
 .then(function (responseText) {
    return JSON.parse(responseText);
})
 .then(function (obj) {
    console.log(obj.value.joke);
})
 .catch(function (err) {
    console.log(err.message);
});
Copy the code

The getJSON function takes a URL address and requests JSON data. However, the requested data is in text format, so json.parse is used to convert it into an object in the callback of the first THEN method, and processing is done in the callback of the second THEN method.

http://api.icndb.com/jokes/random is a random jokes API, you can try: primer..

5. To summarize

Promise is an asynchronous programming solution added to ES6 that allows you to write more elegant, readable, and maintainable programs. Promise has been used everywhere, and I think mastering it is a basic skill for a competent Javascript developer.

6. Reference links

JavaScript Promise: Introduction

Tasks, microtasks, queues and schedules

How to escape Promise Hell

An Overview of JavaScript Promise

ES6 Promise: The Promise syntax is introduced

Ruan Yifeng teacher Promise object details

About me: Personal homepage Simple book nuggets