Asynchronous (async) is the opposite of Synchronous (sync).

Asynchronous concept

In traditional single-threaded programming, programs run synchronously (synchronization does not mean that all steps are run at the same time, but that the steps are executed sequentially in a sequence of control flows). The concept of asynchrony is not guaranteed synchronization, that is, the execution of an asynchronous process is no longer sequential with the original sequence. In simple terms, synchronous execution is carried out in code order, while asynchronous execution is not carried out in code order. Asynchronous execution is more efficient. (One is a relay race, the other is a sprint race)

Asynchrony is the sending of a child thread from the main thread to complete a task.

Scenarios that require asynchronous programming

We often use child threads to do things that might take long enough for the user to notice, such as reading a large file or making a network request. Because the child thread is independent of the main thread, blocking does not affect the main thread. However, the child thread has one limitation: once it is fired, it will lose synchronization with the main thread, and we cannot determine its end. If something needs to be done after the end, such as processing information from the server, we cannot merge it into the main thread. To solve this problem, asynchronous manipulation functions in JavaScript often implement the result processing of asynchronous tasks through callback functions.

The callback function

The setTimeout in the following example program is a long (3 seconds) process. The first argument is a callback function, and the second argument is the number of milliseconds. After this function is executed, a child thread is generated, which waits for 3 seconds and then executes the callback function print.

function print(a) {
    document.getElementById("demo").innerHTML = "SUCCESS!";
}
setTimeout(print, 3000);
console.log("TEST");

// Another way of writing:
setTimeout(function () {
    document.getElementById("demo").innerHTML = "SUCCESS!";
}, 3000);
console.log("TEST");
Copy the code

SetTimeout waits for 3 seconds in the child thread. The main thread does not stop after the setTimeout function is executed.

JavaScript Promise

Promise is a class provided by ECMAScript 6 to make writing complex asynchronous tasks more elegant. Since Promise is a new addition to ES6, some older browsers don’t support it until Apple’s Safari 10 and Windows’ Edge 14 versions and beyond begin supporting ES6 features.

Constructing Promise objects

Create a new Promise object:

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

Creating a new Promise object doesn’t seem to show how it can “elegantly write complex asynchronous tasks.” All of the asynchronous tasks we’ve encountered have been asynchronous once. What if you need to call an asynchronous function multiple times? For example, if the string is printed in three intervals, the first interval is 1 second, the second interval is 4 seconds, and the third interval is 3 seconds:

// Output First after 1 second
setTimeout(function () {
    console.log("First");
    
    // Then output Second 4 seconds later
    setTimeout(function () {
        console.log("Second");
        
        // Output Third after the last 3 seconds
        setTimeout(function () {
            console.log("Third");
        }, 3000);
    }, 4000); 
}, 1000);
Copy the code

This program does this, but it does it using a function waterfall. As you can imagine, in a complex program, the waterfall implementation of the program both maintenance and exception handling is extremely tedious, and the indentation format becomes very cumbersome.

Now use Promise to do the same:

new Promise(function (resolve, reject) {
    
    // Output First after 1 second
    setTimeout(function () {
        console.log("First");
        
        // Resolve is called here
        resolve(a); },1000);
    
}).then(function () {

    // Then 4 seconds later it prints Second, which returns a Promise object
    return new Promise(function (resolve, reject) {
    
        setTimeout(function () {
            console.log("Second");
            
            // Resolve is called here
            resolve(a); },4000);
    });    
}).then(function () {
    
    // Output Third after the last 3 seconds
    setTimeout(function () {
        console.log("Third");
    }, 3000);
});
Copy the code

The above sample code is long, so you don’t need to understand it next. It will be analyzed below, focusing on: Promise changed the nested code into sequential code.

The use of the Promise

The Promise constructor takes only one argument and is a function that will be run asynchronously directly after construction, so it is called the starter function. The start function takes two arguments, resolve and reject.

When a Promise is constructed, the initiation function is executed asynchronously:

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

This program will simply print Run. Resolve and reject are functions, where resolve means everything is ok, reject is called when an exception occurs:

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");
});

/ / output
a / b = 0
End
Copy the code

The Promise class has.then(),.catch(), and.finally() methods, all of which take a function as an argument..then() adds the function from the argument to the normal execution sequence of the current Promise, .catch() is the exception handling sequence that sets the Promise, and.finally() is the sequence that must be executed at the end of the Promise execution. .then() functions are passed in order, and any exceptions are jumped directly to the catch sequence:

new Promise(function (resolve, reject) {
    // output 1111
    console.log(1111);
    
    // we pass 2222 as a parameter to the function in the following then argument.
    // If resolve is not called, the function will not proceed
    resolve(2222);
    console.log("Resolve executes, this will still execute, do not treat resolve as a return.")}).then(function (value) {
    // output 2222 from above
    console.log(value);
    
    // we pass 3333 to tnen (resolve, return)
    return 3333;
}).then(function (value) {
    // output 3333
    console.log(value);
    
    // This throws an error
    throw "An error";
}).catch(function (err) {
    // Here the catch catches the error thrown above
    console.log(err);
});

/ / output
1111Resolve executes, it will still execute here, do not treat resolve asreturnLook at2222
3333
An error
Copy the code

Resolve () can place an argument to pass a value to the next THEN, and functions in that then can return a value to pass to that. However, if a Promise object is returned in then, the next THEN will act on that returned Promise, as you can see from the timer example. The reject() argument typically passes an exception to a later catch function that handles the exception.

But please note the following two points:

  • resolverejectThe start function is the only scope of thethenAnd other sequences;
  • resolverejectYou can’t stop the start function, rememberreturn.

Promise function

The above “timer” program that uses promises looks longer than a waterfall of functions, so we can write its core as a Promise function:

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

Then you can implement the program boldly:

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

Such functions that return a Promise object are called Promise functions and are often used to develop libraries based on asynchronous operations.

Answer frequently asked Questions (FAQs)

Q: Can the then, catch, and finally sequences be reversed? A: Yes, the effect is exactly the same. This is not recommended; it is better to write programs ina then-catch-finally order.

Q: Can the other two types of blocks be used more than once? A: Yes, finally executes in the same order as then, but catch blocks 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 does the then block break? A: The then block is executed downward by default. A return cannot be interrupted. A catch can be interrupted by A throw.

Q: When is it appropriate to use promises instead of traditional callback functions? A: When you need to perform multiple asynchronous operations sequentially, for example, if you want to use an asynchronous method to detect the user name and then the password, you need to asynchronously detect the user name and then asynchronously detect the password.

Q: Are promises a way to convert asynchrony to synchronization? A: Not at all. Promise is just a better programming style.

Q: When do we need to write another THEN instead of programming the current THEN? A: When you need to call another asynchronous task.

An asynchronous function

Async functions are a specification of the ECMAScript 2017 (ECMA-262) standard and are supported by almost all browsers except Internet Explorer.

In Promise we wrote a Promise function:

function print(delay, message) {

    return new Promise(function (resolve, reject) {
        setTimeout(function () {
            console.log(message);
            resolve(a); }, delay); }); }Copy the code

It then outputs three lines of text at different intervals:

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

Using async functions can make this code look better:

async function asyncFunc(a) {
    await print(1000."First");
    await print(4000."Second");
    await print(3000."Third");
}
asyncFunc(a);Copy the code

This makes asynchronous operations as easy as synchronous operations! The answer this time is yes. An async function can use an await instruction. An await instruction must be followed by a Promise, and the asynchronous function will pause in the Promise execution until it is finished. The asynchronous function works exactly the same as the Promise native API, but is easier for programmers to read.

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

async function asyncFunc(a) {
    try {
        await new Promise(function (resolve, reject) {
            throw "Some error"; // 或者 reject("Some error")
        });
    } catch (err) {
        console.log(err);
        // Outputs Some error}}asyncFunc(a);Copy the code

If a Promise has a normal return value, the await statement returns it as well:

async function asyncFunc(a) {
    let value = await new Promise(
        function (resolve, reject) {
            resolve("Return value"); }); console.log(value);
}
asyncFunc(a);/ / output
Return value
Copy the code

The Promise object represents an asynchronous operation and has three states: Pending, Resolved, and Rejected. Mark the promise as resolverd by calling resolve(data) in the callback and then((data)=>{// do something}).

ECMAscript 6 natively provides Promise objects. The Promise object represents events that will happen in the future and is used to deliver messages for asynchronous operations.

Promise objects have two characteristics

  1. The status of an object is not affected. The Promise object represents an asynchronous operation with three states:
  • Pending: Indicates the initial state, not the success or failure state.
  • This is a pity: which means that the operation will be completed successfully.
  • Rejected: Indicates that the operation fails.

Only the result of an asynchronous operation can determine the current state, and no other operation can change the state. That’s where the name “Promise” comes from. Its English name means “Promise,” indicating that nothing else can change it.

  1. Once the state changes, it never changes again, and you can get this result at any time. There are only two possibilities for a Promise object to change state: from Pending to Resolved and from Pending to Rejected. As long as those two things happen, the state is frozen, it’s not going to change, it’s going to stay the same. If you add a callback to the Promise object, you’ll get the same result immediately, even if the change has already occurred. This is quite different from an Event, which has the characteristic that if you miss it and listen again, you will not get the result.

Promise the pros and cons

With the Promise object, asynchronous operations can be expressed as a flow of synchronous operations, avoiding layers of nested callback functions. In addition, Promise objects provide a unified interface that makes it easier to control asynchronous operations.

Promise also has some downsides. First, there is no way to cancel a Promise; once it is created, it is executed immediately and cannot be cancelled halfway through. Second, if you don’t set a callback function, errors thrown inside a Promise won’t be reflected externally. Third, when you are in a Pending state, you have no way of knowing what stage of progress you are currently in (just beginning or just finishing).

Promise to create

To create a Promise object, instantiate it by calling the Promise constructor with new.

Here are the steps to create a promise:

var promise = new Promise(function(resolve, reject) {
    // Asynchronous processing
    // After processing, call resolve or reject
});
Copy the code

The Promise constructor contains one parameter and a callback with resolve and reject parameters. Perform some operation (asynchronous, for example) in the callback, calling resolve if all is well, reject otherwise.

var myFirstPromise = new Promise(function(resolve, reject){
    Resolve (...) is called when asynchronous code executes successfully. Reject (...) is called when asynchronous code fails.
    // In this case, use setTimeout(...) To simulate asynchronous code, which could be an XHR request or some HTML5 API method
    setTimeout(function() {resolve("Success!"); // The code works fine!
    }, 250);
});
 
myFirstPromise.then(function(successMessage){
    // successMessage values resolve(...) The value passed by the method.
    // The successMessage parameter doesn't have to be a string, but this is just an example
    document.write("Yay! " + successMessage);
});
Copy the code

You can call the promise.then() method on an already instantiated Promise object, passing the resolve and reject methods as callbacks.

Promise.then () is the most commonly used method for promises.

promise.then(onFulfilled, onRejected)
Copy the code

Promise simplifies handling of errors, and the code above can also be written like this:

promise.then(onFulfilled).catch(onRejected)
Copy the code

Promise AJAX

Here is an example of an AJAX operation implemented with a Promise object.

function ajax(URL) { return new Promise(function (resolve, reject) { var req = new XMLHttpRequest(); req.open('GET', URL, true); req.onload = function () { if (req.status === 200) { resolve(req.responseText); } else { reject(new Error(req.statusText)); }}; req.onerror = function () { reject(new Error(req.statusText)); }; req.send(); }); } var URL = "/try/ajax/testpromise.php"; This is a big pity. Then (function ondepressing (value){document.write(' content is: '+ value); }). Catch (function onRejected(error){document.write(' error: '+ error); });Copy the code

In the code above, the resolve and reject methods are called with arguments. Their arguments are passed to the callback function. The reject argument is usually an instance of an Error object, while the resolve argument may be another Promise instance in addition to the normal value, as shown below.

var p1 = new Promise(function(resolve, reject){
  // ... some code
});
 
var p2 = new Promise(function(resolve, reject){
  // ... some code
  resolve(p1);
})
Copy the code

In the code above, p1 and P2 are both instances of Promise, but P2’s resolve method takes P1 as an argument, and p1’s state is passed to P2. If the state of P1 is pending, the p2 callback will wait for the state to change. If P1 has fulfilled fulfilled or Rejected, then P2’s callback function will be executed immediately.

Promise.prototype. Then method: chain operation

The promise.prototype. then method returns a new Promise object, so it can be chained.

getJSON("/posts.json").then(function(json) {
  return json.post;
}).then(function(post) {
  // proceed
});
Copy the code

The code above specifies two callback functions in turn, using the THEN method. After the first callback completes, the second callback is passed the result as an argument. If the previous callback returns a Promise object, the later callback waits for the Promise object to run before calling it.

getJSON("/post/1.json").then(function(post) {
  return getJSON(post.commentURL);
}).then(function(comments) {
  // Process comments
});
Copy the code

This design allows nested asynchronous operations to be easily rewritten from “horizontal development” to “downward development” of the callback function.

Promise.prototype.catch method: Catch errors

The promise.prototype. catch method is an alias to promise.prototype. then(null, Rejection) to specify a callback when an error occurs.

getJSON("/posts.json").then(function(posts) {
  // some code
}).catch(function(error) {
  // Handle the error that occurred while the previous callback function was running
  console.log('Error! ', error);
});
Copy the code

Errors in the Promise object are “bubbling” and are passed backwards until they are caught. That is, an error is always caught by the next catch statement.

getJSON("/post/1.json").then(function(post) {
  return getJSON(post.commentURL);
}).then(function(comments) {
  // some code
}).catch(function(error) {
  // Handle errors in the first two callback functions
});
Copy the code

Promise.all method, promise.race method

The promise.all method is used to wrap multiple Promise instances into a new Promise instance.

var p = Promise.all([p1,p2,p3]);
Copy the code

In the code above, the promise. all method takes an array as an argument, with p1, P2, and p3 being instances of the Promise object. (The promise. all method does not have to take an array as an argument, but it must have an iterator interface and return each member as a Promise instance.)

The state of P is determined by P1, P2 and P3, which can be divided into two cases.

  1. onlyp1,p2,p3This will be a big pity,pThis will become a pityp1,p2,p3To form an array of return values passed topThe callback function of.
  2. As long asp1,p2,p3One of them is rejected,pThe state of rejected becomes rejected, and the return value of the first rejected instance is passed to p’s callback function.

Here is a concrete example.

// Generate an array of Promise objects
var promises = [2.3.5.7.11.13].map(function(id){
  return getJSON("/post/" + id + ".json");
});
 
Promise.all(promises).then(function(posts) {
  // ...  
}).catch(function(reason){
  // ...
});
Copy the code

The promise.race method also wraps multiple Promise instances into a new Promise instance.

var p = Promise.race([p1,p2,p3]);
Copy the code

In the above code, the state of P changes as long as one of the first instances of P1, P2, and P3 changes state. The return value of the first changed Promise instance is passed to the return value of P.

If the parameters of the promise. all and promise. race methods are not Promise instances, the promise. resolve method described below is called to convert the parameters to a Promise instance, and further processing is done.

Promise. Resolve, Promise. Reject

Sometimes you need to turn an existing object into a Promise object, and the promise.resolve method does this.

var jsPromise = Promise.resolve($.ajax('/whatever.json'));
Copy the code

The code above converts jQuery to generate the Deferred object into a new ES6 Promise object.

If the parameter of the Promise. Resolve method is not an object with the then method (also known as the Thenable object), a new Promise object will be returned and its state will be fulfilled.

var p = Promise.resolve('Hello');
 
p.then(function (s){
  console.log(s)
});
// Hello
Copy the code

The code above generates a new instance P of the Promise object, which is fulfilled, so the callback function will execute immediately. The parameters of the Promise. Resolve method are the parameters of the callback function.

If the argument to the promise. resolve method is an instance of the Promise object, it is returned unchanged.

The promise.Reject (Reason) method also returns a new Promise instance with a state of Rejected. The reason argument to the promise. reject method is passed to the instance callback.

var p = Promise.reject('Wrong');
 
p.then(null, function (s){
  console.log(s)
});
/ / make a mistake
Copy the code

The above code generates an instance of the Promise object in the rejected state, and the callback is executed immediately.

So that’s a summary of Promise objects and their use.

Refer to the link

Reference link :🔗

  • Promise object
  • Promise
  • ECMAScript 6 profile
  • JavaScript tutorial
  • Asynchronous JavaScript programming
  • JavaScript object of Promise
  • JSON tutorial
  • JavaScript JSON