reference

How does V8 implement async/await?

www.yuque.com/ruochuan12/…

Promise, Generator and Async await are different and related

  • Callback is not easy to read
  • Promise then too many, poor semantic
  • Generator needs next to trigger functions or co to execute
  • Async /await await gets a promise of resolve state to trigger the code to continue executing
  • Generator + (co) + promise = async/await

generator

Es6.ruanyifeng.com/#docs/gener…

The core principle is coroutines

More lightweight than threads exist, a process multiple threads, a thread multiple coroutines, but the thread by the operating system kernel control, coroutines by program code control, better performance.

Only one coroutine can be executed by a thread at a time. The next method of the traverser object runs as follows. (1) When a yield expression is encountered, the following operation is paused, and the value immediately following the yield expression is used as the value of the returned object’s value property. (2) The next time the next method is called, the execution continues until the next yield expression is encountered. (3) If no new yield expression is encountered, the function is run until the end of the return statement, and the value of the expression following the return statement is used as the value of the returned object’s value property. (4) If the function does not have a return statement, the value attribute of the returned object is undefined. Semantically, the first next method is used to start the traverser object, so it doesn’t take arguments.

The first next argument is invalid, but yields the yield expression, and the second next argument replaces the first yield expression

# the firstyieldIt takes twice for next to finish executionfunction* getResult() {
3
yield 'getUserID'/ / 2.5
5
yield 'getUserName'
7
return 'name'

}
1
let result = getResult()
2
console.log(result.next().value)
4
console.log(result.next().value)
6
console.log(result.next().value) //name

Copy the code

The getResult function is not executed all at once, but rather the global code and functions are executed alternately.

The asterisked function is a generator function. When yield is encountered, V8 returns the content after the keyword to the external function and suspends the generator function. The controller is handed over to the external code, which executes to the generator function’s next method, and the controller is handed over to the generator function.

co

The next method of a generator function is recursively executed until it cannot be executed, passing a value

Github1s.com/tj/co/blob/…

# Done and value concepts for generatorfunction* getResult() {
    yield 'getUserID'
    return 'name'
}

let result = getResult()
console.log(result.next().value)
//Result.next (). Value is getUserID, result.next(). Done is Boolean to determine whether the generator function is complete://www.jianshu.com/p/651922749334

let a = yield x
result.next(100)// Arguments can be used instead of yield and the followingGet a =100Arguments can be passed to the generator function through Next to dynamically adjust the logic insideCopy the code
# https://www.cnblogs.com/mengff/p/12787981.html
/** * slice() reference. */

var slice = Array.prototype.slice;

/** * Expose `co`. */

module.exports = co['default'] = co.co = co;

/**
 * Wrap the given generator `fn` into a
 * function that returns a promise.
 * This is a separate function so that
 * every `co()` call doesn't create a new,
 * unnecessary closure.
 *
 * @param {GeneratorFunction} fn
 * @return {Function}
 * @api public* /

co.wrap = function (fn) {
  createPromise.__generatorFunction__ = fn;
  return createPromise;
  function createPromise() {
    return co.call(this, fn.apply(this.arguments)); }};/**
 * Execute the generator function or a generator
 * and return a promise.
 *
 * @param {Function} fn
 * @return {Promise}
 * @api public* /
//co(generatorFunc, 'oops ');
function co(gen) {
  var ctx = this;
  var args = slice.call(arguments.1);/ / / 'oh'

  // we wrap everything in a promise to avoid promise chaining,
  // which leads to memory leak errors.
  // see https://github.com/tj/co/issues/180
  return new Promise(function(resolve, reject) {
          // Execute generator function with generatorFunc(' oops ')
    if (typeof gen === 'function') gen = gen.apply(ctx, args);
  
    if(! gen ||typeofgen.next ! = ='function') return resolve(gen);// Not generator, return
	// generatorFunc(' oops ').next
    onFulfilled();

    / * * *@param {Mixed} res
     * @return {Promise}
     * @api private* /

    function onFulfilled(res) {
      var ret;
      try {
        ret = gen.next(res);//generatorFunc(' oops ').next()
      } catch (e) {
        return reject(e);
      }
      next(ret);//generatorFunc(' oops ').next() is executed in the next function
      return null;
    }

    / * * *@param {Error} err
     * @return {Promise}
     * @api private* /

    function onRejected(err) {
      var ret;
      try {
        ret = gen.throw(err);
      } catch (e) {
        return reject(e);
      }
      next(ret);
    }

    /**
     * Get the next value in the generator,
     * return a promise.
     *
     * @param {Object} ret
     * @return {Promise}
     * @api private* /
	
    function next(ret) {
        Resolve (ret.value) if the generator function fails to yield
      if (ret.done) return resolve(ret.value);
        // Encapsulate the value of the result of generatorFunc(' oops ').next() as a promise
      var value = toPromise.call(ctx, ret.value);
        Resolve (ret.value)
      if (value && isPromise(value)) return value.then(onFulfilled, onRejected);
      return onRejected(new TypeError('You may only yield a function, promise, generator, array, or object, '
        + 'but the following object was passed: "' + String(ret.value) + '"')); }}); }/**
 * Convert a `yield`ed value into a promise.
 *
 * @param {Mixed} obj
 * @return {Promise}
 * @api private* /

function toPromise(obj) {
    // if obj is a string, an error is reported, requiring a different type
  if(! obj)return obj;
  if (isPromise(obj)) return obj;
  if (isGeneratorFunction(obj) || isGenerator(obj)) return co.call(this, obj);
  if ('function'= =typeof obj) return thunkToPromise.call(this, obj);
  if (Array.isArray(obj)) return arrayToPromise.call(this, obj);
  if (isObject(obj)) return objectToPromise.call(this, obj);
  return obj;
}
When you yield a string, reject is emitted because it is not a promise and CO did no conversion
// let fn = function (cb) {
// console.log('fnxx')
// cb && cb(null, '123', '2', '3')
// return 'fn'
// }
// let genera = function* () {
// yield 'err'
// let value = yield fn
// return value
// }
// co(genera)
//UnhandledPromiseRejectionWarning: TypeError: You may only yield a function, promise,
//generator, array, or object, but the following object was passed: "err"

/**
 * Convert a thunk to a promise.
 *
 * @param {Function}
 * @return {Promise}
 * @api private* /
//fn must be fn=(cb)= "{cb(null,'res')} to trigger the following 2 function

function thunkToPromise(fn) {
  var ctx = this;
  return new Promise(function (resolve, reject) {
      //http://qiutianaimeili.com/html/page/2019/05/54g0vvxycyg.html 涉及thunk
    fn.call(ctx, function (err, res) {
        / / 2
      if (err) return reject(err);
      if (arguments.length > 2) res = slice.call(arguments.1);
      resolve(res);
    });
  });
}

/**
 * Convert an array of "yieldables" to a promise.
 * Uses `Promise.all()` internally.
 *
 * @param {Array} obj
 * @return {Promise}
 * @api private* /

function arrayToPromise(obj) {
  return Promise.all(obj.map(toPromise, this));
}

/**
 * Convert an object of "yieldables" to a promise.
 * Uses `Promise.all()` internally.
 *
 * @param {Object} obj
 * @return {Promise}
 * @api private* /

function objectToPromise(obj){
  var results = new obj.constructor();
  var keys = Object.keys(obj);
  var promises = [];
  for (var i = 0; i < keys.length; i++) {
    var key = keys[i];
    var promise = toPromise.call(this, obj[key]);
    if (promise && isPromise(promise)) defer(promise, key);
    else results[key] = obj[key];
  }
  return Promise.all(promises).then(function () {
    return results;
  });

  function defer(promise, key) {
    // predefine the key in the result
    results[key] = undefined;
    promises.push(promise.then(function (res) { results[key] = res; })); }}/**
 * Check if `obj` is a promise.
 *
 * @param {Object} obj
 * @return {Boolean}
 * @api private* /

function isPromise(obj) {
  return 'function'= =typeof obj.then;
}

/**
 * Check if `obj` is a generator.
 *
 * @param {Mixed} obj
 * @return {Boolean}
 * @api private* /

function isGenerator(obj) {
  return 'function'= =typeof obj.next && 'function'= =typeof obj.throw;
}

/**
 * Check if `obj` is a generator function.
 *
 * @param {Mixed} obj
 * @return {Boolean}
 * @api private* /
 
function isGeneratorFunction(obj) {
  var constructor = obj.constructor;
  if (!constructor) return false;
  if ('GeneratorFunction'= = =constructor.name| | 'GeneratorFunction'= = =constructor.displayName) return true;
  return isGenerator(constructor.prototype);
}

/**
 * Check for plain object.
 *
 * @param {Mixed} val
 * @return {Boolean}
 * @api private* /

function isObject(val) {
  return Object == val.constructor;
}
Copy the code
The internal code of the next function consists of only four lines of commands. The first line is the last step to check if the current is a Generator and return if so. Line two, make sure that the return value of each step is zeroPromiseObject. In the third line, add the callback function to the return value using the then method, and then call the next function again via the ondepressing function. The fourth line, in the case that the arguments do not meet the requirements (the arguments are not Thunk functions andPromiseObject), willPromiseThe object's state is changed to Rejected, terminating execution.Copy the code

Simple implementation

co(generatorFun,'params')
function co(gen){
    let ctx = this
    let params = Array.prototype.slice.call(argumengt,1)
    return new Promise((resolve,reject) = >{
        gen = gen.apply(ctx,params)
        onFulfilled()
        function onFulfilled(res){
            let ret = gen.next(res)
            next(ret)
        }
        function next(ret){
          let {value,done} = ret
            if(done){
                return resolve(value)
            }else{
                return Promise.resolve(value).then(onFulfilled)
            }  
        }
    })
}
Copy the code

Sichuan elder brother version

# https://github1s.com/lxchuan12/koa-analysis/blob/master/koa/examples/co-generator/co-simple-3.html
function coSimple(gen){
    const ctx = this;
    const args = Array.prototype.slice.call(arguments.1);
    gen = gen.apply(ctx, args);
    console.log(gen, 'gen');

    return new Promise((resolve, reject) = > {

      onFulfilled();

      function onFulfilled(res){
        const ret = gen.next(res);
        next(ret);
      }

      function next(ret) {
        constpromise = ret.value; promise && promise.then(onFulfilled); }}); }Copy the code

Arjun version

#https://www.yuque.com/ruochuan12/bssbzg/ci77ui
const co = gen= > new Promise((resolve, reject) = > {
	function next(data) {
        let { value, done } = gen.next(data);
        if (done) {
            resolve(value);
        } else {
            Promise.resolve(value).then(next, reject);
        }
    }
    next(); // We need to execute it once and recursively
})
Copy the code