preface

Javascript is a single-threaded language, so when you’re executing a task, all the tasks have to queue up and then execute one by one, and in javascript you have synchronous code, and asynchronous code, as the name implies, synchronous code, which is code that executes on this, asynchronous code might not execute immediately, Javascript has a task queue, which is used to store asynchronous code. Tasks in the task queue are divided into priorities. Microtasks have higher priorities than macroTasks. The main thread will finish executing the synchronous code first and put the asynchronous code into the task queue. When the synchronous code is finished, it polls the task queue and asks for the micro task first. If there is a micro task, it will execute the micro task, if there is no macro task.

// Asynchronous codesetTimeout(function() {// belongs to macro task console.log()'hello world3'); }, 0); New Promise(resolve => {// belong to microtask console.log('hello world4'); // The Promise object will execute immediately.'hello world5'); }).then(data => {console.log(data)}); // Synchronize the codefunction main(){
  console.log('hello world');
}
console.log('hello world1');
console.log('hello world2');
main();
Copy the code

The output is:

hello world4
hello world1
hello world2
hello world
hello world5
hello world3
Copy the code

In the order described above, the synchronous code is executed first, so hello world4 is printed and then Hello world1,hello world2, Hello World is printed and then the asynchronous code of the task queue is executed, polling the microtask to see if there is any code to execute, Since the Promise object is a microtask, execute it first and print Hello world5, then execute the macro task code and setTimeout code to print Hello world3

This simple example walks you through the execution of javascript code, hopefully to help you understand asynchrony. The Promise objects involved are covered in detail in this article.

This article may have a lot of code, all the code involved is on my Github

Github.com/sundial-dre…

So let’s get back to the five ways async is implemented in Javascript, and using Ajax as an example, we can write async in Javascript in the following ways

  • Callback
  • Publish and subscribe model
  • Promise object
  • Es6 generator functions
  • async/awit

1. Callback

Callback functions are the most common in asynchronous Javascript programming. Because Javascript functions are first-class citizens, they can be passed as parameters, so there are callback functions. As those familiar with NodeJS know, there are a lot of callback functions involved. For example, some animation processing, when the animation is finished, then perform a callback, or connect to a database, for example

function load(url,callback){
   //something
   setTimeout(callback,3000); } load();'xxx'.function() {
   //do something
   console.log('hello world')})Copy the code

3S perform the callback, and the content of the callback is up to you

Let’s look at another Ajax example (code)

//ajax_callback.js

function ajax(object, callback) {
  functionIsFunction (func) {// whether it is a functionreturn typeof func === 'function';
  }

  functionIsObject (object) {// Whether it is an objectreturn typeof object === 'object';
  }

  functionToQuerystring (data) {/ / object into a query string For example, {a: 1, b: 2} = > = 1 & b = 2 or {a: [1, 2], b: 3} = > a = 1 & 2 & b = a = 3if(! isObject(data) || ! data) throw new Error('data not object');
    var result = ' ';
    for (var key in data) {
      if (data.hasOwnProperty(key)) {
        if(isObject(data[key]) && ! Array.isArray(data[key])) throw new Error('not support error'); // Remove objectsif (Array.isArray(data[key])) { 
          data[key].forEach(function (v) {
             result += key + '=' + v + '&'   
          });
        } else {
          result += key + '=' + data[key] + '&'; }}}returnresult.substr(0, result.length - 1); / / remove &} var url = object. At the end of the url | |' ';
  var method = object.method.toUpperCase() || 'GET';
  var data = object.data || Object.create(null);
  var async = object.async || true;
  var dataType = object.dataType || 'json'; // The corresponding data type is optional json,text, XML var XHR = new XMLHttpRequest(); url = ajax.baseUrl + url; data = toQuerystring(data); method ==='GET' && (url += '? '+ data) && (data = null); //get request => url followed by? A =1&b=2 this try {xhr.open(method, url, async); method ==='POST' && (xhr.setRequestHeader('Content-Type'.'application/x-www-form-urlencoded')); // Set the request header to application/x-www-form-urlencoded type console.log(data); xhr.send(data); xhr.onreadystatechange =function() {// Listen on eventsif (this.readyState === 4) {
        if (this.status === 200)
          if (isFunction(callback))
            switch (dataType) {
              case 'json': { callback(JSON.parse(this.responseText)); // Execute the passed callback when donebreak
              }
              case 'text': {
                callback(this.responseText);
                break
              }
              case 'xml': {
                callback(this.responseXML);
                break
              }
              default: {
                break;
              }
            }
      }
    }
  } catch (e) {
    console.log(e);
  }
}

ajax.get = function(url, data, callback) {//get this({url: url, method: callback)'GET', data: data}, callback);
};
ajax.post = function(url, data, callback) {// Post this({url: url, method: callback)'POST', data: data}, callback);
};
ajax.baseUrl = ' ';

Copy the code

The above is a complete Ajax example. When ajax completes, the callback is implemented using KOA as a simple server that simulates the ajax response, which will be used in the following examples

//koa_test_server.js

const Koa = require('koa');
const Router = require('koa-router');
const bodyparser = require('koa-bodyparser');
const app = new Koa();
const api = new Router();
api.get('/api/test1', async CTX => {// Process the get request ctx.res.setheader ('Access-Control-Allow-Origin'.The '*'); // Allow cross-domain accesslet querystring = ctx.querystring;
  console.log(querystring);
  ctx.body = JSON.stringify({
    errno: false,
    data: 'it is ok',
    message: `you send me ${querystring} type is GET`
  });
}).post('/api/test2', async CTX => {// Process the POST request ctx.res.setheader ('Access-Control-Allow-Origin'.The '*'); // Allow cross-domain accesslet data = ctx.request.body;
  console.log(data);
  ctx.body = JSON.stringify({
    errno: false,
    data: 'it is ok',
    message: `you send me ${JSON.stringify(data)} type is POST`
  })
});
app.use(bodyparser());
app.use(api.routes()).use(api.allowedMethods());
app.listen(3000, () => {
  console.log('listen in port 3000')});Copy the code

Simple use is as follows

//test.html

 ajax.baseUrl = 'http://localhost:3000';
  ajax.get('/api/test1',{name: 'dpf', age: 19},function (data) {
    //do something such as render page
    console.log(data);
  });
  ajax.post('/api/test2',{name: 'youname', age: 19}, function (data) {
    //do something such as render page
    console.log(data);
  });
Copy the code

The results are as follows:

The good thing about callbacks is that they’re easy to write, but the bad thing is that too many callbacks can create callback hell and make code scale-out and less readable. But there are many other uses for callbacks, and they’re also the most common way to implement asynchronous Javascript.

2. Publish and subscribe

Publish-and-subscribe is a design pattern that is not unique to javascript, so javascript can use publish-and-subscribe to do asynchrony, and other languages such as C++, Java, python, PHP can also do asynchrony.

A brief introduction of publish-subscribe pattern, release subscription is two things, namely, publish and subscribe, imagine, there’s a delivery, you can order food, this is the subscription, is when you takeaway ready, someone will give you call you to take delivery, this is released, simply put, a subscription model, there is a event pool, to give you subscribe to (registration) events, You are notified when an event you subscribe to occurs, and you can then process the event, modeled as follows

The next step is to simply implement the publish subscribe pattern

// async_event.js // single-object Event is equivalent to the Event center const Event =functionConst EventPool = new Map(); Const isFunction = func => typeof func === const isFunction = func => typeof func ==='function'; Const on = (event callback) = > {/ / register event EventPool. Get (event) | | EventPool. Set (event, []);if (isFunction(callback)) {
      EventPool.get(event).push(callback);
    }
    else {
      throw new Error('callback not is function')}}; Const addEventListenr = (event, callback) => {//on alias on(event, callback)}; const emit = (event, ... Args) => {// Trigger (publish) the event // make the event trigger an asynchronous process, i.e. execute after the synchronous code // also oksetTimeout(fn,0)
    Promise.resolve().then(() => {
      let funcs = EventPool.get(event);
      if(funcs) { funcs.forEach(f => f(... args)) }else {
        throw new Error(`${event}not register`) } }) }; const send = (event, ... Args => {//emit method alias emit(event,... args) }; Const removeListener = event => {// Delete the event promise. resolve(() => {// Delete the event is also an asynchronous processif(event){
        EventPool.delete(event)
      }else{
        throw new Error(`${event} not register`)
      }
    })
  };

  return {
    on, emit, addEventListenr, send
  }
}();

Copy the code

Simple to use

 Event.on('event', data => {
      console.log(data)
    });
setTimeout(() => {
      Event.emit('event'.'hello wrold')
    },1000);
Copy the code

After 1s, the event is triggered, and hello World is displayed

Using the publish-subscribe model, modify the previous Ajax example

. xhr.onreadystatechange =function() {// Listen on eventsif (this.readyState === 4) {
        if (this.status === 200)
            switch (dataType) {
              case 'json': {
                Event.emit('data '+method,JSON.parse(this.responseText)); // Trigger the eventbreak
              }
              case 'text': {
                Event.emit('data '+method,this.responseText);
                break
              }
              case 'xml': {
                Event.emit('data '+method,this.responseXML);
                break
              }
              default: {
                break;
              }
            }
         }
    }
......
Copy the code

Use the following

//test.html // Register Event.'data GET',data => {
      //do something such as render page
      console.log(data)
    });

Event.on('data POST',data => {
      //dosomething such as render page console.log(data) }); // Use ajax ajax.baseurl ='http://localhost:3000';
ajax.get('/api/test1',{name: 'dpf', age: 19});
ajax.post('/api/test2',{name: 'youname', age: 19});
Copy the code

The advantages of the publish-subscribe model are centralized event management and easy modification, but the disadvantages are that the code is less readable and the events are prone to conflict.

3. Promise

Promise objects are a solution to asynchronous programming that is more reasonable and powerful than traditional callback functions and events. Promises, simply a container that holds the result of an event that will end in the future, provide a unified API that allows asynchronous operations to be handled in the same way as callback functions. Two features of Promisel objects:

##1. The object state is not affected by the outside world. The Promise object has three states :pending, fulfilled, and Rejected. When the asynchronous operation has a result, you can specify the pending state to the fulfilled state or the transition from the pending state to the Rejected state. Once the state becomes a pity or Rejected, the Promise object will not change.

##2. Once the state changes, it does not change, and this result can be obtained at any time.

The basic format

letPromise = new promise ((resolve, reject) => {// The promise object accepts a function try {setTimeout(() => {// Simulate an asynchronous operation. Resolve ('hello world'); // Resolve () can only accept one parameter at most. To pass multiple parameters, write it as an array or an object. Resolve ([1,2,2]) or resolve({data,error}) reject(); // This is a pity. This is a pity. }catch (e) {reject(e) {reject(e) {reject(e) {reject(e) {reject(e) {reject(e)}}); Promise.then ((data) => {console.log(data) //resolve()},(err) => {console.log(err) //reject()});Copy the code

A few methods of the Hello World Promise object after 1s

This is a pity (pity,rejected). There is a big pity (data) and Rejected (err), which are single-parameter callback functions, respectively. The pity corresponds to the callback that is implemented when the asynchronous task is completed, and the Rejected corresponds to the callback that is implemented when the asynchronous task fails. The parameter of the pity function is the value passed by the resolve() function, and the parameter of the Rejected function is the value passed by the reject() function.

##2. Catch (Rejected) : the alias of then(null, Rejected) catches errors in the Promise object

##3. Promise.resolve(data):等价于new Promise(resolve => {resolve(data)})

# # 4. Promise. All ([promise1, promise2,…, promisen]) : used for more than one Promise object execution, execution time take the slowest one, such as:

let promise1 = new Promise(resolve => {
  setTimeout(() => {
    resolve(1);
  }, 1000);
});
let promise2 = new Promise(resolve => {
  setTimeout(() => {
    resolve(2)
  }, 2000)
});
let promise3 = new Promise(resolve => {
  setTimeout(() => {
    resolve(3)
  }, 3000)
});
letstart = Date.now(); Promise.all([promise1, promise2, promise3]).then(([data1, data2, Data3]) => {// Use array deconstruction to get the data console.log(' datas = 'for each Promise object${data1}.${data2}.${data3} total times = ${Date.now() - start}ms`);
});
Copy the code

The output is datas = 1,2,3 total times = 3000ms

# # 5. Promise. Race ([promise1, promise2,…, promisen]), and Promise. All are similar, but it take a Promise the fastest in the object.

##6.Promise. Reject (err): equivalent to new Promise((resolve,reject) => reject(err))

Now that you have a basic understanding of the Promise object, you can use it to replace the pattern of callback functions, such as an image load example

// callback formfunctionAsyncLoadImage_callback (url,callback) {var proxyImage = new Image(); // proxyimage.src = url; proxyImage.onload = callback; } asyncLoadImage_callback('xxx'.function () {
  image.src = 'xxx'}); //Promise object formfunction asyncLoadImage_Promise(url) {
  return new Promise((resolve,reject) => {
    var proxyImage = new Image();
    proxyImage.src = url;
    proxyImage.onload = resolve;
    proxyImage.onerror = reject;
  })
}
asyncLoadImage_Promise('xxx')
  .then(() => {
  image.src = 'xxx'}). Catch (err => console.log(err));Copy the code

The benefits of using Promise objects are obvious, except for the hassle of writing them.

Next, you’ll see how to convert the callback function form to the Promise object form

##1. Convert the callback function form to the Promise object form

//promisify.js
//callback => Promise
/**
 *
 * @param fn_callback
 * @returns {function(... [*]): Promise<any>} */functionPromisify (fn_callback) {// Accept a function with a callback, usually in the last argumentif(typeof fn_callback ! = ='function') throw new Error('The argument must be of type Function.');
  return function(... Args) {// Return a functionreturnNew Promise((resolve, reject) => {// Return Promise object try {if(args.length > fn_callback.length) reject(new Error('arguments too much.')); fn_callback.call(this,... args,function(... args) { args[0] && args[0] instanceof Error && reject(args[0]); //nodejs callback, the first parameter is err, Error object args = args. Filter (v => v! == undefined && v ! == null); Resolve (args)}.bind(this)); } catch (e) {reject(e)}})}}Copy the code

Simple to use

// Nodejs fs.readfile is an examplelet asyncReadFile = promisify(require('fs').readFile);
asyncReadFile('async.js').then(([data]) => { console.log(data.toString()); }, err => console.log(err)); // Convert the above asyncLoadImage_callback to an examplelet asyncLoadImage = promisify(asyncLoadImage_callback);
asyncLoadImage.then(() => {
  image.src = 'xxx'});Copy the code

##2. The Promise object form is converted to the callback function form

//callbackify.js
//Promise => callback
/**
 * 
 * @param fn_promise
 * @returns {Function}
 */
function callbackify(fn_promise) {
  if(typeof fn_promise ! = ='function') throw new Error('The argument must be of type Function.');
  return function(... args) {letcallback = args.pop(); // Return a function whose last argument is a callbackif(typeof callback ! = ='function') throw new Error('The last argument must be of type Function.');
    if(fn_promise() instanceof Promise){fn_promise(args). Then (data => {callback(null,data)// callback execution}). Catch (err => { Callback (err,null)// Callback execution})}else{
      throw new Error('function must be return a Promise object'); }}}Copy the code

Simple to use

let func =  callbackify(timer => new Promise((resolve, reject) => {
  setTimeout(() => {resolve('hello world')},timer);
}));
func(1000,function(err,data) {console.log(data)// Prints hello world after 1s});Copy the code

The next step is to rewrite the previous Ajax example by changing the callback form to the Promise form, either directly or using the promisify function

## First way

//ajax_promise.js
function ajax(object) {
  return new Promise(function (resolve,reject) {
  ....
    try {
     ....
      xhr.onreadystatechange = function() {// Listen on eventsif (this.readyState === 4) {
          if (this.status === 200) {
            switch (dataType) {
              case 'json': {
                resolve(JSON.parse(this.responseText));
                break
              }
              case 'text': {
                resolve(this.responseText);
                break
              }
              case 'xml': {
                resolve(this.responseXML);
                break
              }
              default: {
                break; }}}else{
            reject(new Error('error'))
          }
        }
      }
    } catch (e) {
      reject(e)
    }
  });
}

ajax.get = function(url, data) {//get methodreturn this({url: url, method: 'GET', data: data});
};
ajax.post = function(url, data) {// Postreturn this({url: url, method: 'POST', data: data});
};
ajax.baseUrl = ' ';
Copy the code

Simple to use

//test.html
ajax.baseUrl = 'http://localhost:3000';
ajax.get('/api/test1',{name: 'dpf', age: 19}).then(data => {
      console.log(data)
    });
ajax.post('/api/test2',{name: 'youname', age: 19}).then(data => {
      console.log(data)
    });
Copy the code

## Second way

//test.html
ajax = promisify(ajax);
ajax.baseUrl = 'http://localhost:3000';
ajax.get = (url,data) => ajax({url: url, method: 'GET', data: data});
ajax.post = (url,data) => ajax({url: url, method: 'POST', data: data});
ajax.get('/api/test1', {name: 'dpf', age: 19}).then(([data]) => {
    console.log(data)
  });
ajax.post('/api/test2', {name: 'youname', age: 19}).then(([data]) => {
    console.log(data)
  });
Copy the code

Promise objects are currently a popular asynchronous solution. The code is no longer scale-out compared to callback functions, and there is no callback hell. There are a number of benefits, but there are some downsides: they are more difficult to write (compared to callback), but Promise objects are still an important part of javascript. Hopefully, this has given the reader a basic idea of the Promise object.

The Generator function is an asynchronous programming solution provided by ES6 that behaves like a state machine. A simple example

function *gen(){// Declare a generatorlet t1 = yield "hello"; // use yield to generate console.log(t1);let t2 = yield "world";
   console.log(t2);
}
letg = gen(); /*next() returns a {value,done} object whose value is the yield expression,doneValues fortrue/falseIs used to indicate whether * generation ends */letx = g.next(); //{value:"hello".done:false} Start the generator /** * by passing a value to the next() function, where the value is passed to the first yield expression equivalent to the gen functionlet t1 = "aaaa"* /let y = g.next("aaaa"); //{value:"world".done:false}
g.next("bbbb"); //{value:undefined,done:true}
console.log(x.value,y.value);
Copy the code

The output

aaaa
bbbb
hello world
Copy the code

In the above example, if we treat the gen function as a state machine, we skip to the next state, the next yield expression, by calling the next() method, and pass the value to the next() function to pass the value to the previous state, the result of the previous yield expression. Before introducing the asynchrony of Generator functions, I will briefly introduce some methods of Generator functions

##1.next() method: A generator function can accept a yield expression that has no value, or always returns undefined. The next() function can accept an argument that will be used as the yield expression value.

Throw () method: Throws an error outside the function and catches it inside the function. For example,

function *gen1(){
    try{
        yield;
    }catch(e){
        console.log('Internal capture')}}let g1 = gen1();
g1.next();
g1.throw(new Error());
Copy the code

Print out the

Internal captureCopy the code

##3.return() method: Returns the given value and terminates the generator. For example,

function *gen2(){
    yield 1;
    yield 2;
    yield 3;
}
letg2 = gen1(); g2.next(); //{value:1,done:false} g2.return(); //{value:undefined,done:true} g2.next(); //{value:undefined.done:true}
Copy the code

##4.yield* expression: Calls another generator function in a generator function. For example,

function *gen3(){
    yield 1;
    yield 2;
    yield 3;
}
function *gen4(){ yield 4; yield * gen3(); yield 5; } // equivalent tofunction *gen4(){
    yield 4;
    yield 1;
    yield 2;
    yield 3;
    yield 5;
}
Copy the code

In the use of the Generator (Generator) functions do asynchronous, first introduced coroutines this concept, can be understood as “collaborative function”, the collaborators cheng essence function, but this child function can perform to a half, can be suspended, the executive power to other functions, such as recovery of executive power, later also can continue to perform, Much like threads, in c++/python/ Java, the unit of a thread is also a subfunction (Java’s run method). Switching between threads is like switching between functions, but it’s very expensive to switch between threads. You have to store a lot of thread-related stuff. Or lightweight threads.

The execution process of coroutines is roughly as follows:

##1. Coroutine A starts execution.

The execution of coroutine A is paused in the middle of the execution, and the execution authority is transferred to coroutine B.

##3.(after a period of time) Coroutine B returns execution authority.

##4. Coroutine A resumes execution

Coroutine A is an asynchronous task because it is executed in multiple segments.

The next section describes using Generator functions to implement coroutines asynchronously. Let’s start with a simple example

const fs = require('fs');
function* gen(){// Generator functionlet data = yield asyncReadFile(__dirname+'/ajax_promise.js'); console.log(data); // If the file is successfully read, outputletdata2 = yield timer(1000); console.log(data2); // After 1s output hello world}let it = gen();
it.next();
functionTimer (time){// Asynchronous tasksetTimeout(() => it.next('hello world'),time)
}
functionAsyncReadFile (url) {fs.readFile(url,(err,data) => {it.next(data.tostring ())})}Copy the code

Let data = yield asyncReadFile(__dirname+’/ajax_promise.js’); let data = yield asyncReadFile(__dirname+’/ajax_promise ‘); Data waits for the result of asyncReadFile. If the result is returned, the output, gen, continues. However, the implementation of each asynchronous function, such as asyncReadFile, becomes troublesome

const promisify = require('./promisify');
function timer(time,callback){
    setTimeout(() => callback(), time)
}
const asyncReadFile = promisify(require('fs').readFile); // Convert callback to Promise const asyncTimer = promisify(timer);function *gen() {let [data] = yield asyncReadFile('./a.mjs'); // Generate a Promise object console.log(data); yield asyncTimer(1000); console.log('hello world');
}
let g = gen();
let {value} = g.next(); //{value:asyncReadFile('./a.mjs'),done:false}
value.then(data => {//相当于asyncReadFile('./a.mjs').then(data => {})
    let{value} = g.next(data); //{value:asyncTimer(1000),done:false} value. Then (data => {1000). Then (data => {}) g.ext (data); //{value:undefined,done:true}})});Copy the code

As you can see from the above example of the Promise object, there is a more general implementation for asynchronous processing, namely a generator executor,

//run.js
functionRun (gen){// Pass in a generator functionlet g = gen();
    function next(data){
        let result = g.next(data);
        let {value,done} = result;
        if(done) returnvalue; //donefortrueTo end the recursionif(Array.isArray(value)) value = Promise.all(value); // If the yield expression is followed by an array, it can be converted to promise.allif(! Then ((data) => {next(data); then((data) => {next(data); // recursive call}); } next(); // Start generator}Copy the code

Using the run executor function, we simply need to run(gen) to run the above gen. Finally, let’s continue rewriting the previous Ajax example, this time using a Generator function

//test.html
  ajax = promisify(ajax);
  ajax.baseUrl = 'http://localhost:3000';
  ajax.get = (url,data) => ajax({url: url, method: 'GET', data: data});
  ajax.post = (url,data) => ajax({url: url, method: 'POST', data: data});
  run(function* () {let [[data1],[data2]] = yield [ajax.get('/api/test1', {name: 'dpf', age: 19}),ajax.post('/api/test2', {name: 'youname', age: 19})]; Promise.all console.log(data1,data2)});Copy the code

Using Generator functions is definitely a better way to handle asynchrony than callback and Promise objects. Without callback callback hell, the Promise object’s long then chain, asynchrony code looks just like synchronous code, is much more readable and maintainable.

5. Async /await(the ultimate solution to javascript asynchrony)

In ES6, Generator functions are used for async. In ES2017, two keywords async/await are provided for async to make async more convenient. Async /await is essentially a syntactic sugar of Generator functions. Async is equivalent to the run function written earlier (the function executing the Generator function) and await is equivalent to yield. The await expression can only be followed by a Promise object. If it is not a Promise object, the Promise. Resolve method will make it a Promise object. Async decorates function, which returns a Promise object. Await must be placed inside async modified functions, just as yield can only be placed inside Generator functions. A simple example

Const timer = time => new Promise((resolve,reject) => {setTimeout(() => resolve('hello world'),time)
});

async function main() {/ / async functionlet start = Date.now();
    letdata = await timer(1000); // await = asyncwaitThis is asynchronous waiting (although a yield variant) that returns the value when the Promise object has a value, the data in resolve(data) of the Promise object, as the result of the await expression console.log(data,'time = ',Date.now() - start,'ms')// Will print hello world time = 1002 ms} main();Copy the code

We can see that async/await is very convenient to use. In fact, the principle of async/await is very simple, which is to wrap Generator functions and actuators together. The implementation is as follows

//spawn. Js // A variation of the previous run function, but with error handling, returns a Promise objectfunction spawn(genF){
    return new Promise((resolve,reject) => {
        let g = genf();
        function next(nextF){
            let next;
            try{
                next = nextF();
            }catch(e){
                reject(e)
            }
            if(next.done) return resolve(next.value);
            Promise.resolve(next.value)
                   .then(data => next(() => g.next(data)))
                   .catch(err => next(() => g.throw(err)));
        }
        next(() => g.next(undefined))
    })
}
Copy the code

So async function main() {} is equivalent to function main() {return spawn(function *() {})}.

Let’s look at an example of using async/await to improve on the previous Ajax

//test.html
ajax = promisify(ajax);
ajax.baseUrl = 'http://localhost:3000';
ajax.get = (url,data) => ajax({url: url, method: 'GET', data: data});
ajax.post = (url,data) => ajax({url: url, method: 'POST', data: data});
 
(async function() {
    let [data1,data2] = await Promise.all([ajax.get('/api/test1', {name: 'dpf', age: 19}),ajax.post('/api/test2', {name: 'youname', age: 19})]);
    console.log(data1,data2)
})() 
Copy the code

Callback: Easy to write, but too many callbacks can create callback hell and make code scale-out that is not easy to maintain or understand

##2. Publish and subscribe mode: through the implementation of an event manager, convenient management and modification of events, different events correspond to different callbacks, trigger events to achieve asynchronous, but will produce some naming conflicts, events triggered everywhere, the code may not be readable.

# # 3. Promise object: Nature is used to solve the callback generated code lateral extension, and the problem of readability is not strong. Then approach to replace the callback, and then the method of parameters also have limits, so settled, the callback parameter is not easy to determine problem, faults, personal feel, write up may not be so easy, but to write well, It’s much more convenient to use.

##4.Generator functions: Remember that I first encountered Generator functions in Python, and that I learned the concept of coroutines and the use of Generator functions to implement asynchrony in Python. Javascript feels a bit borrowed from Python. It does solve the problem of asynchrony in JavaScript, but relies on the executor function.

##5. Async /await: This is probably the best way to deal with async in javascript, making asynchronous code write like synchronous code, readable and maintainable.

All the code in the final article is on my github github.com/sundial-dre…

Hopefully, this article has given you some insight into JavaScript asynchrony.