ES6 Asynchronous Flow history (Part 2)

generator/co

Before WE do that, let’s take a look at es6

  function* foo() {
      yield 1;
      yield 2;
      yield 3;
      return console.log(4)}var it = foo();
  it.next();
  it.next();
  it.next();
  it.next(); / / 4
Copy the code
function *doSomething() {

    consoleThe log (" start ")yield
    console.log (' finish ')}var func1 = doSomething();
func1.next();
func1.next();

function* getStorckPrice(stock) {

    while (true) {
        yield Math.random() * 100; }}varPriceGenerator = getStockPrice (" IBM ");var limitPrice = 15;
var price = 100;
while (price > limitPrice) {
    price = priceGenerator.next().value;
    console.log( `this generator return ${price}` );
}
console.log( `buying at ${price}` );

Copy the code

Principle of the generator

We googled it and found a tool from Facebook: regeneratorRuntime, which compiles generator functions for ES6.

Let’s download it

  npm install - g regenerator
Copy the code

Then create a generator, JS file

    function* hiGenerator() {
        yield '1';
        yield '2';
        return '3';
    }
Copy the code

Execute the following code

  regenerator geberator.js > generator - es5.js
Copy the code

This will definitely not run because of the regeneratorRuntime we encountered. wap.

    "use strict";

    var _marked =
        /*#__PURE__*/
        regeneratorRuntime.mark(hiGenerator);

    function hiGenerator() {
        return regeneratorRuntime.wrap(function hiGenerator$(_context) {
            while (1) {
                switch (_context.prev = _context.next) {
                    case 0:
                        _context.next = 2;
                        return '1';

                    case 2:
                        _context.next = 4;
                        return '2';

                    case 4:
                        return _context.abrupt("return".'3');

                    case 5:
                    case "end":
                        return _context.stop();
                }
            }
        }, _marked);
    }
Copy the code

A call to iterator.next that primarily uses iterators.

regenerator –include-runtime geberator.js > generator-es5.js

Or we could come up with a dot thing 700 lines after compilation, and we strip out the main logic

var _marked =
    /*#__PURE__*/
    regeneratorRuntime.mark(hiGenerator);
Copy the code

There are

  function hiGenerator() {
      return regeneratorRuntime.wrap(function hiGenerator$(_context) {
          while (1) {
              switch (_context.prev = _context.next) {
                  case 0:
                      _context.next = 2;
                      return '1';

                  case 2:
                      _context.next = 4;
                      return '2';

                  case 4:
                      return _context.abrupt("return".'3');

                  case 5:
                  case "end":
                      return _context.stop();
              }
          }
      }, _marked);
  }
Copy the code

Now let’s look at the Mark function

    exports.mark = function(genFun) {
        if (Object.setPrototypeOf) {
            Object.setPrototypeOf(genFun, GeneratorFunctionPrototype);
        } else {
            genFun.__proto__ = GeneratorFunctionPrototype;
            if(! (toStringTagSymbolin genFun)) {
                genFun[toStringTagSymbol] = "GeneratorFunction";
            }
        }
        genFun.prototype = Object.create(Gp);
        return genFun;
    };
Copy the code

GeneratorFunctionPrototype and Gp variables, we see the corresponding code:

function Generator(){}

function GeneratorFunction(){}

Their function is to maintain the chain of relationships, consistent with the original. Such as:

    function* f() {}
    var g = f();
    console.log(g.__proto__ === f.prototype); // true
    console.log(g.__proto__.__proto__ === f.__proto__.prototype); // true
Copy the code

We mount the next, throw, and return functions on the GP object.

  function defineIteratorMethods(prototype) {["next"."throw"."return"].forEach(function(method) {
          prototype[method] = function(arg) {
              return this._invoke(method, arg);
          };
      });
  }
Copy the code
function wrap(innerFn, outerFn, self, tryLocsList) {

    var protoGenerator = outerFn && outerFn.prototype instanceof Generator ? outerFn : Generator;
    var generator = Object.create(protoGenerator.prototype);
    var context = new Context(tryLocsList || []);

    generator._invoke = makeInvokeMethod(innerFn, self, context);

    return generator;
}
Copy the code

So when hell = hllGenerator(), we’re actually executing the wrap function, and the wrap function returns a generator, a generator object, which has outerFn, outerFn. Prototype (genfun.prototype); getFun.prototype (next ()); _invoke = makeInvokeMethod(innerFn, self, context); The innerFun here is the function wrapped in the wrap. HiGenerator The main function inside makeInvokeMethod is the invoke function that performs operations on different context states. Hi. Next ()=>record=> innerFn. Call (self, context)=>abrupt, we record object. This object has a complete method.


Syntactically, generator has two characteristics:

  • The function keyword is preceded by an asterisk.

  • The yield keyword is used inside the function body to define different internal states.

    Generator is meant to be an iterator generator that exits at yield, preserving the context. Control the execution process of functions, manually pause and resume code execution. The drawback of generator is that it has no actuator and is not designed for flow control, so it has the address of GIthub

  const co = require('co')
  const Promise = require('bluebird')

  const fs = Promise.promisifyAll(require('fs'))

  async function main(){
    const contents = co(function*{
      var result = yield.fs.readFilAsyn('myfiles.js'.'utf8')
      returnresult; })}Copy the code

async/await

The execution process of async functions

1) await helps us deal with promises, either returning an honoured value or throwing an exception;

2) await the whole async function suspends while waiting for the promise to be fulfilled and re-executes the following code after the promise is fulfilled.

const readAsync = util.promisify(fs.readFile)
async function init() {
    let data = await readAsync('./package-lock.json')
    data = JSON.parse(data)
    console.log(data.name)
}
init()
Copy the code

Async/await principle

We compile with Babel and convert async/await into something.

/ / the source code
function tell() {
    return new Promise((reslove, reject) = > {
        if (true) {
            reslove(1)}else {
            reject(2)}}}var a = (
    async() = > {await tell().then((res) = > {
            console.log('res', res);
        }).catch(err= > {
            console.log('err: ', err); })})console.log('a', a())

// After conversion
"use strict";

function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
    try {
        var info = gen[key](arg);
        var value = info.value;
    } catch (error) {
        reject(error);
        return;
    }
    if (info.done) {
        resolve(value);
    } else {
        Promise.resolve(value).then(_next, _throw); }}function _asyncToGenerator(fn) {
    return function() {
        var self = this,
            args = arguments;
        return new Promise(function(resolve, reject) {
            var gen = fn.apply(self, args);

            function _next(value) {
                asyncGeneratorStep(gen, resolve, reject, _next, _throw, "next", value);
            }

            function _throw(err) {
                asyncGeneratorStep(gen, resolve, reject, _next, _throw, "throw", err);
            }
            _next(undefined);
        });
    };
}

function tell() {
    return new Promise(function(reslove, reject) {
        if (true) {
            reslove(1);
        } else {
            reject(2); }}); }var a =
    /*#__PURE__*/
    function() {
        var _ref = _asyncToGenerator(
            /*#__PURE__*/
            regeneratorRuntime.mark(function _callee() {
                return regeneratorRuntime.wrap(function _callee$(_context) {
                    while (1) {
                        switch (_context.prev = _context.next) {
                            case 0:
                                _context.next = 2;
                                return tell().then(function(res) {
                                    console.log('res', res); }) ["catch"] (function(err) {
                                    console.log('err: ', err);
                                });

                            case 2:
                            case "end":
                                return _context.stop();
                        }
                    }
                }, _callee);
            }));

        return function a() {
            return _ref.apply(this.arguments); }; } ();console.log('a', a());
Copy the code

Yeah, it’s a long list of moments, but don’t panic. Let’s start with a rational (panic) analysis. It defines three functions asyncGeneratorStep, _asyncToGenerator, and tell wow. Promsie is a function we know well and are familiar with. Let’s look at the two remaining functions. So let’s do a little bit of an overview of asyncGeneratorStep and this should be based on what we’re doing in different states. The _asyncToGenerator and tell functions allocate the function variables passed in. (Not doing specific things).

Now let’s look at this pivot function defined by A. The switch mode is adopted to distribute events according to different states.

So we can write a simplified version of that

var myAsync = generator= > {
  // Notice that iterator.next() returns the value of the object as promiseAjax(), a promise
  const iterator = generator();

  // Handle controls the suspension - execution of async
  const handle = iteratorResult= > {
    if (iteratorResult.done) return;

    const iteratorValue = iteratorResult.value;

    // Consider only asynchronous requests that return a promise
    if (iteratorValue instanceof Promise) {
      // Recursively call handle, and iterator.next() after the promise is fulfilled to continue the generator execution
      iteratorValue
        .then(result= > handle(iterator.next(result)))
        .catch(e= >iterator.throw(e)); }};try {
    handle(iterator.next());
  } catch (e) {
    console.log(e); }}; myAsync(function* () {
  try {
    const a = yield Promise.resolve(1);
    const b = yield Promise.reject(a + 10);
    const c = yield Promise.resolve(b + 100);
    console.log(a, b, c); // output 1,11,111
  } catch (e) {
    console.log("Error:", e); }});Copy the code

Generator () generates its controller. The handle function is called, passing interator.next(), and the yield generator hangs again, passing the promsie to handle, and interatorValue.then(), Pass the value of the asynchronous request to interator.next() and execute the generator again. Finally, if (iteratorresult.done) is true, exit.

The use of await is divided into three general types:

  • await + async
  • await + Promise
  • await + co + generaor
// This is await with promsie
function get1(){
  return 1
}
function get2(){
  return 2
}
function get3(){
  return 3
}
function get4(){
  return 4
}


(async function async(){
// try{
// } catch(err){
// console.log('err: ', err);
/ /}
  const g1 =  get1();
  const g2 =  get2();
  const g3 =  get3();
  const g4 =  get4();
  let sum  =0;
  let data = await Promise.all([g1,g2,g3,g4])
  for(v of data){
    sum = sum+v;
  }
  console.log(sum)
})()
Copy the code

Afterword.

Welcome to follow the wechat public account! For better communication.Copy the code

Related knowledge reference materials

Promises/A+ Promises/A+

Promises/A+ – Translation 1

Promises/A+ Promises/A+

JS execution stack

Four approaches to Asynchronous programming in Javascript