First of all, make an advertisement to your friends, the circle and the upcoming circle of big guys all understand, a class and various platforms such as Geek,Kaikeba and other low price resources, package update, in the hands of the first-line development buddy, at the same time for the upcoming candidates have interview guidance technology refers to the service, plus he sa!

The Javascript language’s execution environment is single-threaded. That is, only one task can be completed at a time. If there are multiple tasks, queue them up and execute them one by one. After the previous task is completed, the next task is executed.

This implementation mode is simple and the implementation environment is relatively simple. However, with the increasing complexity of front-end business and the increasing number of transactions and requests, such a single-threaded execution mode is bound to be inefficient in complex business. As long as one task takes a long time, the following tasks have to wait in a queue, which will delay the execution of the whole program. The common browser non-response (suspended animation) is often due to a piece of Javascript code running for a long time (such as an infinite loop), resulting in the entire page stuck in this place, other tasks can not be performed.

In order to avoid and solve this problem, JS language divides task execution mode into asynchronous and synchronous. “Synchronization mode” is the mode of the previous section, the latter task waits for the end of the previous task, and then executes. The execution order of the program is consistent and synchronized with the order of the task. Asynchronous mode “is completely different, each task has one or more of the callback function (the callback), before the end of a task, not to perform a task, after but the callback function, after a task is before the end of a task before execution, so the program execution order the order is not consistent with the task, asynchronous.

“Asynchronous mode” is very important. On the browser side, long operations should be performed asynchronously to avoid browser unresponsiveness. The best example is Ajax operations. On the server side, “asynchronous mode” is even the only mode, because the execution environment is single-threaded, and if all HTTP requests are allowed to be executed synchronously, the server performance deteriorates dramatically and becomes unresponsive very quickly.

1. Callback function

The most basic method of asynchronous programming.

First, you need to declare that the callback function is an implementation, not an implementation specific to the asynchronous pattern. Callbacks can also be used in synchronous (blocking) scenarios and other scenarios.

A callback function is a callback function. A callback is a function that is passed as an argument to another function and is executed after its parent function has Completed.

A callback function is literally an argument that is passed to another function as an argument. When that function finishes executing, it executes the passed function. This process is called a callback.

In JavaScript, A callback function is specifically defined when function A is passed as an argument (function reference) to another function B that executes function A. Let’s say function A is called A callback function. If there is no name (function expression), it is called an anonymous callback function.

Here’s a real-life example: you’re walking your girlfriend home after a date. When you’re leaving, you’re likely to say, “Text me when you get home. I’m worried about you.” And then your girlfriend actually sent you a message when she got home. This is actually a callback process. You leave a parameter function to your girlfriend, and then your girlfriend goes home, and the home action is the main function. She has to get home, the main function executes, then the function that was passed in, and then you get a message.

Suppose you have two functions, F1 and F2, the latter waiting for the execution result of the former.

f1();
f2(); 
Copy the code

If f1 is a time-consuming task, consider rewriting F1 and writing F2 as a callback function for F1.

Function f1(callback){setTimeout(function () {// f1's task code callback(); }, 1000); }Copy the code

The execution code looks like this:

f1(f2);
Copy the code

In this way, we change the synchronous operation into asynchronous operation. F1 will not block the program, which is equivalent to executing the main logic of the program first and postponing the execution of time-consuming operations.

Another example:

Function A(callback) {callback(); Console. log(' I am the main function '); } function B(){setTimeout("console.log(' I am the callback function ')", 3000); } // Call the main function and pass function B into A(B); // I am the main function and I am the callback functionCopy the code

In the above code, we first define the main function and the callback function, and then call the main function, passing in the callback function.

When we define the main function, we tell the code to execute the callback() function first, but the output is the contents of the callback function. This means that the main function doesn’t have to wait for the callback to finish, and can continue to execute its own code. So callbacks are usually used for time-consuming operations. Like Ajax requests, like processing files.

Here’s a more mundane example:

< p style = "max-width: 100%; clear: both; min-height: 1em; </ span style = "box-sizing: border-box; color: RGB (74, 74, 74); line-height: 22px; font-size: 14px! Important; white-space: inherit! Important;" Do you mind if I wait in the next dorm until my classmates come back? Sure, but now you have to waste time waiting instead of doing other things. Turns a non-blocking asynchronous call into a blocking synchronous call. JavaScript's callbacks are used in asynchronous invocation scenarios, where they perform better than polling.Copy the code

Callbacks are usually executed last in the synchronous situation, but may not be executed in the asynchronous situation because the event was not fired or the condition was not met, so ignore the small problem in the previous example, the callback does not have to be executed.

Also add the application scenarios and pros and cons of the callback function:

  • Resource loading: dynamic load js file after the execution of the callback, iframe after the execution of the callback, Ajax operation callback, image loading after the execution of the callback, Ajax and so on.
  • DOM events and Node.js events are based on the callback mechanism (node.js callbacks can be nested at multiple levels).
  • The delay time of setTimeout is zero. This hack is often used, and the function called by setTimeout is actually a callback.
  • Chained calls: Chain call, in the assignment editor (setter) method (or itself has no return value method) is easy to implement chain calls, and device (getter) values are relatively bad implementation chain calls, because you need value is return a pointer to the data you need rather than this, if you want to achieve chain method, can be done using the callback function.
  • Function calls to setTimeout and setInterval return their values. Since both functions are asynchronous, that is: Their call timing is separate from the main flow of the program, so there is no way to wait for their return value in the body. When they are turned on, the program does not stop to wait. Otherwise, the meaning of setTimeout and setInterval would be lost. Only callback can be used. The purpose of the callback is to notify the agent function of the result of the timer execution for timely processing.

The advantage of the callback approach is that it is easy to understand, can bind multiple events, each event can specify multiple callbacks, and can be “decouped”, which facilitates modularity. The downside is that the entire program has to be event-driven, and the running process becomes very unclear.

2. Promise

With the release of the ES6 standard, the solution for handling asynchronous data flows has taken a new turn.

promise

That’s one of them. As we all know in traditional Ajax requests, when there are data dependencies between asynchronous requests, it can lead to ugly layers of callbacks, which can make the code logic confusing for reading and maintenance, commonly known as “callback hell.” On the other hand, error-handling code is often coupled with normal business code, making it extremely ugly. To make programming better, we need to introduce Promises to reduce the complexity of asynchronous programming.

So in a way, promise is an advanced approach to asynchronous programming for the callbacks mentioned above. First, Promise is a specification proposed by CommandJS, whose purpose is to provide a uniform interface for asynchronous programming.

Put simply, the idea is that each asynchronous task returns a Promise object with a THEN method that allows you to specify a callback function. In this form:

f1().then(f2);
Copy the code

For function F1, use Jquery to do the following:

function f1(){var dfd = $.Deferred(); SetTimeout (function () {// f1's task code dfd.resolve(); }, 500); return dfd.promise; }Copy the code

The advantage of writing this way is that the callbacks are chained, the flow of the program is clear, and there are a whole set of methods that can do a lot of powerful things. This is one of the conveniences where Promise handles asynchronous programming.

Another example of making multiple callbacks is:

f1().then(f2).then(f3);
Copy the code

The callback function when an error is specified, of the form:

f1().then(f2).fail(f3);
Copy the code

As a bonus, if you add a callback to a promise if a task has already completed, the callback will execute immediately. So, you don’t have to worry about missing an event or a signal. The disadvantage of this approach is that it is relatively difficult to write and understand.

Talk more about a Promise: A Promise is actually a special Javascript object that reflects “the final value of the asynchronous operation.” “Promise” literally means to expect, so it also represents a Promise that the object will eventually return a value to you whether your asynchronous operation succeeds or not.

Code sample

const promise = new Promise((resolve, reject) => { $.ajax('https://github.com/users', (value) => { resolve(value); }).fail((err) => { reject(err); }); }); promise.then((value) => { console.log(value); },(err) => { console.log(err); }); Then (value => console.log(value)).catch(err => console.log(err));Copy the code

In the example above, the resolve callback is called after the Ajax request succeeds to process the result, and the Reject callback is called to handle the error if the request fails. The Promise object contains three states: pending,fulfilled, and rejected. These three states can be likened to pending, SUCCESS and Error in ajax data request process. The initial state of the request is Pending, indicating that it is waiting for processing to complete. This state is an intermediate state and is one-way and irreversible. The state becomes fulfilled after the value is successfully obtained, and the successfully obtained value is then stored for further processing by calling the callback function passed in by the THEN method. If it fails, the state becomes Rejected and the error can either be thrown or rejected.

The basic syntax for a Promise is as follows

  • Promise instances must implement the THEN method

  • Then () must be able to take two functions as arguments

  • Then () must return a Promise instance

    Eg < script SRC = "https://cdn.bootcss.com/bluebird/3.5.1/bluebird.min.js" > < / script > / / if low version browser does not support the Promise, <script type="text/javascript"> function loadImg(SRC) {var promise = new promise (function (resolve, reject) { var img = document.createElement('img') img.onload = function () { resolve(img) } img.onerror = function () { Reject (' image loading failure ')}. Img SRC = SRC}) return promise} var SRC = 'https://www.imooc.com/static/img/index/logo_new.png' var  result = loadImg(src) result.then(function (img) { console.log(1, img.width) return img }, Function () {console.log('error 1')}).then(function (img) {console.log(2, img.height)}) </script> Source: https://juejin.cn/post/6844903620878532616 the nuggets copyright owned by the author. Commercial reprint please contact the author to obtain authorization, non-commercial reprint please indicate the source.Copy the code

Promise can do more than that, for example, if you have several asynchronous tasks, you need to do task 1 first, then task 2 if it succeeds, and if any task fails, you stop continuing and execute an error handler. To execute such an asynchronous task sequentially, you need to write layers of nested code without a Promise.

With Promise, we simply write job1.then(job2).then(job3).catch(handleError); Job1, job2, and job3 are Promise objects.

For example, we want to load the second image after the first one has been loaded. If one of them fails, we will execute the error function:

Var src1 = 'https://www.imooc.com/static/img/index/logo_new.png' var result1 = loadImg (src1) / / var result1 is Promise object Src2 = 'https://img1.mukewang.com/545862fe00017c2602200220-100-100.jpg' var result2 = loadImg (src2) / / result2 object is a Promise Result1.then (function (img1) {console.log(' the first image has been loaded ', Return result2 // result2}).then(function (img2) {console.log(' the second image has been loading ', img2.width) }).catch(function (ex) { console.log(ex) })Copy the code

The thing to note here is that the THEN method can be called multiple times with the same promise, and the THEN method must return a Promise object. In the previous example, if no Promise instance is returned in plaintext, the default Promise instance is resulT1, and result1.then returns the resulT2 instance, and then executes the result2.then.

Promise’s common method

In addition to executing several asynchronous tasks sequentially, Promise can also execute asynchronous tasks in parallel.

Imagine a page chat system where we need to get the user’s personal information and a list of friends from two different urls. These two tasks can be performed in parallel using promise.all () as follows:

var p1 = new Promise(function (resolve, reject) { setTimeout(resolve, 500, 'P1'); }); var p2 = new Promise(function (resolve, reject) { setTimeout(resolve, 600, 'P2'); }); Then: promise.all ([p1, p2]).then(function (results) {console.log(results); // Get an Array: ['P1', 'P2']});Copy the code

Sometimes, multiple asynchronous tasks are fault-tolerant. For example, you can read a user’s personal information to two urls at the same time and just get the result returned first. In this case, use promise.race () :

var p1 = new Promise(function (resolve, reject) {
    setTimeout(resolve, 500, 'P1');
});
var p2 = new Promise(function (resolve, reject) {
    setTimeout(resolve, 600, 'P2');
});
Promise.race([p1, p2]).then(function (result) {
    console.log(result); // 'P1'
});
Copy the code

Since P1 executes faster, the Promise’s then() will get the result ‘p1’. P2 is still executing, but the result will be discarded.

All accepts an array of Promise objects. When all Promise objects are complete, success is executed uniformly.

Promise.race accepts an array of Promise objects and executes SUCCESS as soon as one of them completes.

Let’s make some changes to the above example to understand both:

var src1 = 'https://www.imooc.com/static/img/index/logo_new.png'
     var result1 = loadImg(src1)
     var src2 = 'https://img1.mukewang.com/545862fe00017c2602200220-100-100.jpg'
     var result2 = loadImg(src2)
     Promise.all([result1, result2]).then(function (datas) {
         console.log('all', datas[0])//<img src="https://www.imooc.com/static/img/index/logo_new.png">
         console.log('all', datas[1])//<img src="https://img1.mukewang.com/545862fe00017c2602200220-100-100.jpg">
     })
     Promise.race([result1, result2]).then(function (data) {
         console.log('race', data)//<img src="https://img1.mukewang.com/545862fe00017c2602200220-100-100.jpg">
     })
Copy the code

If we combine promises, we can combine many asynchronous tasks to execute both in parallel and serial.

Promise.reject(Reason) : Return a new Promise object with the reason value to directly change the state to Rejected.

const promise2 = new Promise((resolve, reject) => {
  reject('Failed');
});

const promise2 = Promise.reject('Failed');
Copy the code

These two ways of writing are equivalent.

Promise.resolve(value): Returns a new Promise object that was resolved. Like reject, the following two are equivalent.

const promise2 = new Promise((resolve, reject) => {
  resolve('Success');
});

const promise2 = Promise.resolve('Success');
Copy the code

Then uses this method to access the value or error cause. Its callback function is used to handle the return value of asynchronous processing.

Catch uses this method to catch the error and handle it.

3. Introduction and usage of Async/Await

Introduction to the

  • Async /await is a new way to write asynchronous code. Previous methods included callbacks and promises.
  • Async /await is implemented based on promises and cannot be used with normal callbacks.
  • Async /await, like promises, is non-blocking.
  • Async /await makes asynchronous code look like synchronous code. That’s the magic.

grammar

This is demonstrated with the Promise example and the asyn/await example:

promise

const makeRequest = () =>
  getJSON()
    .then(data => {
      console.log(data)
      return "done"
    })

makeRequest()
Copy the code

async/await

const makeRequest = async () => {
  console.log(await getJSON())
  return "done"
}

makeRequest()
Copy the code

They are slightly different:

  • The function is preceded by an ayNC keyword. Await keyword can only be used within functions defined by ayNC. The async function implicitly returns a promise whose reosolve value is the value of the function return. (In the example, the reosolve value is the string “done”)

  • Point 1 implies that we cannot use await in the outermost code because it is not inside an async function.

    // Cannot use ‘await’ in the outermost code

    MakeRequest ().then((result) => {// code})

Await getJSON() means that console.log will wait until the promise of getJSON is successfully reosolve before executing.

What are the advantages of async/await over promises

1. The concise

As you can see from the example, using Async/Await clearly saves a lot of code. We don’t need to write.then, we don’t need to write an anonymous function to handle the Promise’s resolve value, we don’t need to define redundant data variables, and we avoid nested code. These small benefits add up quickly, which will become more apparent in later code examples.

2. Error handling

Async/Await lets try/catch handle both synchronous and asynchronous errors. In the following Promise example, try/catch cannot handle json.parse’s error because it is in the Promise. We need to use.catch, so the error handling code is very redundant. And, in our actual production, the code is much more complex.

Const makeRequest = () => {try {getJSON().then(result => {// json.parse may fail const data = json.parse (result) Console. log(data)}) // Uncomment, Catch ((err) => {// console.log(err) //})} catch(err) {console.log(err)}}Copy the code

With aync/await, catch can handle json. parse errors

const makeRequest = async () => {
  try {
    // this parse may fail
    const data = JSON.parse(await getJSON())
    console.log(data)
  } catch (err) {
    console.log(err)
  }
}
Copy the code

3. Conditional statements

In the following example, you need to retrieve the data and then decide whether to return directly or continue to retrieve more data based on the returned data.

const makeRequest = () => {
  return getJSON()
    .then(data => {
      if (data.needsAnotherRequest) {
        return makeAnotherRequest(data)
          .then(moreData => {
            console.log(moreData)
            return moreData
          })
      } else {
        console.log(data)
        return data
      }
    })
}
Copy the code

The code is a headache to look at. Nested (6 layers), parentheses, and return statements can be confusing, but they only need to pass the final result to the outermost Promise.

The above code, written with async/await, can greatly improve readability:

const makeRequest = async () => {
  const data = await getJSON()
  if (data.needsAnotherRequest) {
    const moreData = await makeAnotherRequest(data);
    console.log(moreData)
    return moreData
  } else {
    console.log(data)
    return data    
  }
}
Copy the code

4. The median

You’ve probably seen a scenario where you call PromisE1, use the results returned by PromisE1 to call Promise2, and then use the results of both to call Promise3. Your code will probably look something like this:

const makeRequest = () => {
  return promise1()
    .then(value1 => {
      return promise2(value1)
        .then(value2 => {        
          return promise3(value1, value2)
        })
    })
}
Copy the code

If ValuE1 is not required for PROMISe3, you can simply pave the way for nesting promisers. If you can’t stand nesting, you can avoid deep nesting by putting values 1 & 2 in promise.all:

const makeRequest = () => {
  return promise1()
    .then(value1 => {
      return Promise.all([value1, promise2(value1)])
    })
    .then(([value1, value2]) => {      
      return promise3(value1, value2)
    })
}
Copy the code

This approach sacrifices semantics for readability. There is no reason to put Value1 and Value2 in an array other than to avoid nesting.

With async/await, the code becomes surprisingly simple and intuitive.

const makeRequest = async () => {
  const value1 = await promise1()
  const value2 = await promise2(value1)
  return promise3(value1, value2)
}
Copy the code

5. The error stack

The following example calls multiple promises, assuming an error is thrown somewhere in the Promise chain:

const makeRequest = () => {
  return callAPromise()
    .then(() => callAPromise())
    .then(() => callAPromise())
    .then(() => callAPromise())
    .then(() => callAPromise())
    .then(() => {
      throw new Error("oops");
    })
}

makeRequest()
  .catch(err => {
    console.log(err);
    // output
    // Error: oops at callAPromise.then.then.then.then.then (index.js:8:13)
  })
Copy the code

The error stack returned in the Promise chain gives no clue as to where the error occurred. Worse, it can mislead us; The only function in the error stack is called callAPromise, but it has nothing to do with the error. (File name and line number are still useful).

However, the error stack in async/await points to the function where the error is:

const makeRequest = async () => {
  await callAPromise()
  await callAPromise()
  await callAPromise()
  await callAPromise()
  await callAPromise()
  throw new Error("oops");
}

makeRequest()
  .catch(err => {
    console.log(err);
    // output
    // Error: oops at makeRequest (index.js:7:9)
  })
Copy the code

In a development environment, this is not much of an advantage. However, it can be very useful when analyzing the error log of your production environment. At this point, it is better to know that the error occurred in makeRequest than in the THEN chain.

6. Debugging

Last but not least, async/await makes code debugging easier. Debugging promises can be painful for two reasons:

  • You cannot set a breakpoint in an arrow function that returns an expression

    const markRequest = () => { return callAPromise () .then (() => callAPromise()) .then (() => callAPromise()) .then (() => callAPromise()) .then (() => callAPromise())

    }

  • If you set a breakpoint in a.then block of code, using the Step Over shortcut, the debugger will not skip to the next.then because it will only skip the asynchronous code. With await/async, you don’t need as many arrow functions anymore, so you can skip await statements as you would with synchronized code.

    const markRequest = async () => {
        await callAPromise()
        await callAPromise()
        await callAPromise()
        await callAPromise()
        await callAPromise()
    }
    Copy the code

conclusion

As for different commonly used asynchronous programming processing solutions, my personal view is that appropriate and efficient solutions can be selected according to different business scenarios. Each has its own advantages and disadvantages. There is no need to step on one another.