promise generator async三者关系和区别

2,615 阅读5分钟

promise

  • 有三个状态:pending(进行中)、resolved(成功)、rejected(失败)
  • 无法取消Promise,一旦新建就会立即执行,无法中途取消
  • 当处于Pending状态时,无法得知目前进展到那个阶段
  • promise真正执行回调的时候,定义Promise那部分实际上已经走完了,所以Promise的报错堆栈上下文不太友好

generator

  • ES6 引入,可以暂停和继续执行函数
  • 可以当做一个迭代器(iterator)来用,也可以在内部存一些状态,成为一个状态机
  • yield(关键字)表示暂停 yield表单式本身没有返回值或者说总是返回undefined
  • next方法可以带一个参数,该参数就会被当做上一个yield的返回值

async

  • async 对应的是 * 。

  • await 对应的是 yield 。

  • async/await 自动进行了 Generator 的流程控制。

co.js

/**
 * slice() reference.
 */

var slice = Array.prototype.slice;

/**
 * Expose `co`.
 */

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

/**
 * Execute the generator function or a generator
 * and return a promise.
 *
 * @param {Function} fn
 * @return {Promise}
 * @api public
 */

// 这里是核心代码
function co(gen) {
  // 缓存上下文
  var ctx = this;
  var args = slice.call(arguments, 1)

  // we wrap everything in a promise to avoid promise chaining,
  // which leads to memory leak errors.
  // see https://github.com/tj/co/issues/180
  
  // co() 返回值为 promise
  return new Promise(function(resolve, reject) {
    // 执行一次 generator 函数以获取遍历器
    if (typeof gen === 'function') gen = gen.apply(ctx, args);
    if (!gen || typeof gen.next !== 'function') return resolve(gen);

    // 启动执行
    onFulfilled();

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

    function onFulfilled(res) {
      var ret;
      // 执行 generator 函数的 next 方法,如出错则 reject
      try {
        ret = gen.next(res);
      } catch (e) {
        return reject(e);
      }
      
      // 继续执行 next 函数
      next(ret);
    }

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

    function onRejected(err) {
      var ret;
      // 执行 generator 函数的 throw 方法(用于抛出错误,由 generator 函数内部进行错误处理),如出错则 reject
      try {
        ret = gen.throw(err);
      } catch (e) {
        return reject(e);
      }
      
      // 继续执行 next 函数
      next(ret);
    }

    /**
     * Get the next value in the generator,
     * return a promise.
     *
     * @param {Object} ret
     * @return {Promise}
     * @api private
     */
	
    // 关键所在,自动连续执行 next()
    function next(ret) {
      // generator 函数执行完毕,返回
      if (ret.done) return resolve(ret.value);
      // 每次均将 yield 返回的 value 转换为 promise 
      var value = toPromise.call(ctx, ret.value);
      // 使用 Promise.then 递归连续执行
      // onFulfilled 和 onRejected 函数中会继续调用 next 函数自身
      if (value && isPromise(value)) return value.then(onFulfilled, onRejected);
      // yield 返回的 value 无法转换为 promise 时进行错误处理
      return onRejected(new TypeError('You may only yield a function, promise, generator, array, or object, '
        + 'but the following object was passed: "' + String(ret.value) + '"'));
    }
  });
}

/**
 * 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 的使用示例比较容易理解
// 实际上这里只是作了一层简单的封装,方便传参
co.wrap = function (fn) {
  createPromise.__generatorFunction__ = fn;
  return createPromise;
  function createPromise() {
    // 传入的参数通过 apply 方式作为 fn 的参数执行
    // 然后仍然是调用 co 函数,回到上面的逻辑
    return co.call(this, fn.apply(this, arguments));
  }
};


// 下面是一些功能性的 utils 函数,根据注释便可知晓其用法,此处省略函数具体实现,不作过多赘述

/**
 * Convert a `yield`ed value into a promise.
 */
function toPromise(obj) {}

/**
 * Convert a thunk to a promise.
 */
function thunkToPromise(fn) {}

/**
 * Convert an array of "yieldables" to a promise.
 * Uses `Promise.all()` internally.
 */
function arrayToPromise(obj) {}

/**
 * Convert an object of "yieldables" to a promise.
 * Uses `Promise.all()` internally.
 */
function objectToPromise(obj){}

/**
 * Check if `obj` is a promise.
 */
function isPromise(obj) {}

/**
 * Check if `obj` is a generator.
 */
function isGenerator(obj) {}

/**
 * Check if `obj` is a generator function.
 */
function isGeneratorFunction(obj) {}

/**
 * Check for plain object.
 */
function isObject(val) {}

Promise简单实现

class Promise{
    // 构造器
  constructor(executor){
        // 初始化state为等待态
    this.state = 'pending';
        // 成功的值
    this.value = undefined;
    this.reason = undefined;
      // 成功存放的数组
    this.onResolvedCallbacks = [];
      // 失败存放法数组
    this.onRejectedCallbacks = [];
        // 成功
    let resolve = value => {
        // state改变,resolve调用就会失败
      if (this.state === 'pending') {
        // resolve调用后,state转化为成功态
        this.state = 'fulfilled';
        // 储存成功的值
        this.value = value;
        // 一旦resolve执行,调用成功数组的函数
        this.onResolvedCallbacks.forEach(fn=>fn());
      }
    };
        // 失败
    let reject = reason => {
        // state改变,reject调用就会失败
      if (this.state === 'pending') {
         // reject调用后,state转化为失败态
        this.state = 'rejected';
         // 储存失败的原因
        this.reason = reason;
        // 一旦reject执行,调用失败数组的函数
        this.onRejectedCallbacks.forEach(fn=>fn());
      }
    };
    // 如果executor执行报错,直接执行reject
    try{
        // 立即执行
      executor(resolve, reject);
    } catch (err) {
        // 捕获执行错误
      reject(err);
    }
  }
   // then 方法 有两个参数onFulfilled onRejected
  then(onFulfilled,onRejected) {
    // onFulfilled如果不是函数,就忽略onFulfilled,直接返回value
    onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
    // onRejected如果不是函数,就忽略onRejected,直接扔出错误
    onRejected = typeof onRejected === 'function' ? onRejected : err => { throw err };
     // 声明返回的promise2
    let promise2 = new Promise((resolve, reject) => {
      if (this.state === 'fulfilled') {
        //Promises/A+规定onFulfilled或onRejected不能同步被调用,必须异步调用。我们就用setTimeout解决异步问题
        setTimeout(() => {
          try {
            let x = onFulfilled(this.value);
            // resolvePromise函数,处理自己return的promise和默认的promise2的关系
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      };
      if (this.state === 'rejected') {
        setTimeout(() => {
          try {
            let x = onRejected(this.reason);
            resolvePromise(promise2, x, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      };
      // 当状态state为pending时
      if (this.state === 'pending') {
        // onFulfilled传入到成功数组
        this.onResolvedCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onFulfilled(this.value);
              resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        });
        // onRejected传入到失败数组
        this.onRejectedCallbacks.push(() => {
          setTimeout(() => {
            try {
              let x = onRejected(this.reason);
              resolvePromise(promise2, x, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0)
        });
      };
    });
    // 返回promise,完成链式
    return promise2;
  }
  catch(fn){
    return this.then(null,fn);
  }
}

function resolvePromise(promise2, x, resolve, reject){
  // 循环引用报错
  if(x === promise2){
    // reject报错
    return reject(new TypeError('Chaining cycle detected for promise'));
  }
  // 防止多次调用
  let called;
  // x不是null 且x是对象或者函数
  if (x != null && (typeof x === 'object' || typeof x === 'function')) {
    try {
      // A+规定,声明then = x的then方法
      let then = x.then;
      // 如果then是函数,就默认是promise了
      if (typeof then === 'function') { 
        // 就让then执行 第一个参数是this   后面是成功的回调 和 失败的回调
        then.call(x, y => {
          // 成功和失败只能调用一个
          if (called) return;
          called = true;
          // resolve的结果依旧是promise 那就继续解析
          resolvePromise(promise2, y, resolve, reject);
        }, err => {
          // 成功和失败只能调用一个
          if (called) return;
          called = true;
          reject(err);// 失败了就失败了
        })
      } else {
        resolve(x); // 直接成功即可
      }
    } catch (e) {
      // 也属于失败
      if (called) return;
      called = true;
      // 取then出错了那就不要在继续执行了
      reject(e); 
    }
  } else {
    resolve(x);
  }
}