Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JS基础——异步编程 #16

Open
L-small opened this issue May 4, 2020 · 0 comments
Open

JS基础——异步编程 #16

L-small opened this issue May 4, 2020 · 0 comments

Comments

@L-small
Copy link
Owner

L-small commented May 4, 2020

异步编程

异步编程

异步编程的目标语法就是,让他更像同步编程。所以它也是从异步回调 => Promise => Generator => Async/await。
下面是如何将尽可能像同步的进化史~

异步回调

ajax('xxx', () => {
  ajax('xxx', () => {
    xxxxx
  })
})

Promise的串行调用

promise的串行调用的根本都是实现如下的方式

new Promise((resolve, reject) => {
  // .....
  resolve()
}).then((resolve, reject) => {
  // .....
 return result
})

假设有这些promise需要串行调用

let arr = [() => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log(+new Date)
      resolve()
    }, 1000)
  })
}, () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log(+new Date)
      resolve()
    }, 1000)
  })
}, () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log(+new Date)
      resolve()
    }, 1000)
  })
}]

递归实现:

function iteratorPromise(arr) {
  (function iter() {
    if (arr.length) {
      arr.shift()().then(iter)
    }
  })()
}
iteratorPromise(arr)

普通循环实现:

function iteratorPromise(arr) {
  let res = Promise.resolve();
  for(let i of arr) {
    res = res.then((() => arr[i])
  }
}

reduce实现:

function iteratorPromise(arr) {
  arr.reduce((prePromise, curPromise) => {
    return prePromise.then(() => curPromise)
  }, Promise.resolve())
}

Generator+co库(自动执行函数)

根据Generator每次执行后会把执行权交出,当调用next后又将执行权收回。当配合上自动执行函数则可以将异步代码同步执行。

有两种方式可以做到执行权的交回执行权:

  • 回调函数,将异步操作包装,暴露出回调函数,在回调函数里交回执行权
  • Promise对象,将异步操作包装成Promise对象,用then方法交回执行权

自动执行函数其实就是不断地执行next

function run(fn) {
  g.next().value.then((data) => {
     g.next(data).value.then((data) => {
       g.next(data).value.then((data) => {
         //  循环执行
       })
     })
  })
}

基于Promise对象的自动执行

function run(fn) {
  var g = fn();

  function next(data) {
    var result = g.next(data)
    if (g.done) return resule.value
    result.value.then((data) => next(data))
  }

  next()
}

简单版:

function fetchData(url) {
    return function(cb){
        setTimeout(function(){
            cb({status: 200, data: url})
        }, 1000)
    }
}

function* gen() {
    var r1 = yield fetchData('https://api.github.com/users/github');
    var r2 = yield fetchData('https://api.github.com/users/github/followers');

    console.log([r1.data, r2.data].join('\n'));
}

function run (gen) {
  var gen = gen();
  
  function next(data) {
    var result = gen.next(data);
    if (result.done) return result.value;
    // 如果返回的值是Promise则是通过then来控制继续执行(Promise型)
    if (isPromise(result.value)) {
      result.value.then((data) => {
        next(data)
      })
    } else {
      // 当返回的是函数时(回调函数型)
      result.value(next)
    }
  }

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

完整版:(简版的co库)

function run(gen) {
  // co库就是返回一个Promise对象
  return new Promise((resolve, reject) => {
    // 判断是否是function
    if (typeof gen === 'function') gen = gen()
    // 如果不是函数或者没值了则转为完成态
    if (!gen || typeof gen.next !== 'function') return resolve(gen)

    onFulfilled();

    // onFulfilled和onRejected其实就是为了收集报错,效果跟var result = gen.throw(res);一样
    function onFulfilled(res) {
      var ret;
      // 如果报错捕捉报错
      try {
        ret = gen.next(res)
      } catch (e) {
        return reject(e)
      }
      next(ret)
    }

    function onRejected(err) {
      var ret;
      // 将报错抛出
      try {
        ret = gen.throw(err)
      } catch(e) {
        return reject(e)
      }
      // 继续执行
      next(ret)
    }

    function next(ret) {
      // 状态改为完成状态
      if (ret.done) return resolve(ret.value);
      // 将result.value的值转为Promise
      var value = toPromise(ret.value);
      // 如果还有值(执行完value值是undefined)是Promise则继续循环调用
      if (value && isPromise(value)) return value.then(onFulfilled, onRejected)
      // 记录报错
      return onRejected(new TypeError(`you may only yield a function, promise but the following object was passed: ${String(ret.value)}`))
    }
  })
}

// 通用判断是否是promise
function isPromise(obj) {
  return typeof obj.then === 'function'
}

// 将返回值转为Promise类型
function toPromise(obj) {
  if (isPromise(obj)) return obj;
  if ('function' == typeof obj) return thunkToPromise(obj)
  return obj;
}

// 将普通函数转为Promise
function thunkToPromise(fn) {
  return new Promise((resolve, reject) => {
    fn((err, res) => {
      if (err) return reject(err);
      resolve(res)
    })
  })
}

async/await

async function iteratorPromise(arr) {
  for(let val of arr) {
    await val()
  }
}

for await of

async function iteratorPromise(arr) {
  for await(result of arr) {
     console.log(result)
  }
}

JS异步编程系列

Promise

目的

解决回调地狱

实现:

// 主要用了发布订阅模式
简写版:
var MyPromise = function(fn) {
  const that = this;
  that.state = 'pending'
  that.val = null
  that.resolvedCallBacks = []
  that.rejectesCallBacks = []
  
  function resolve(val) {
    if (val instanceof MyPromise) {
      val.then(resolve, reject)
    }
    if (that.state === 'pending') {
      that.state = 'resolve'
      that.val = val
      that.resolvedCallBacks.map(cb => cb(that.val))
    }
  }
  function reject(val) {
    if (that.state === 'pending') {
      that.state = 'rejecct'
      that.val = val
      that.rejectedCallBacks.map(cb => cb(that.val))
    }
  }
  try {
    fn()
  } catch (err) {
    reject(err)
  }
}

MyPromise.prototype.then = function(onFulfilled, onReject) {
  const that = this;
  const val = that.val
  onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : val => val
  onReject = typeof onFulfilled === 'function' ? onFulfilled : (reason) => { throw reason }

  if (that.state === 'pending') {
    that.resolvedCallBacks.push(onFulfilled)
    that.rejectedCallBacks.push(onReject)
  }
  if (that.state === 'resolve') {
    that.resolvedCallBacks.push(() => {
      try {
        const nextPromise = onFulfilled(val);
        if (nextPromise instanceof MyPromise) {
          nextPromise.then(resolve, reject)
        } else {
          resolve(val)
        }
      } catch (reason) {
        reject(reason)
      }
    })
  }
  if (that.state === 'reject') {
    that.rejectedCallBacks.push(() => {
      try {
        const nextPromise = onReject(val);
        if (nextPromise instanceof MyPromise) {
          nextPromise.then(resolve, reject)
        } else {
          resolve(val)
        }
      } catch (reason) {
        reject(reason)
      }
    })
  }
}
Promise.prototype.catch = function(onRejected) {
  const that = this
  return that.then(null, onRejected);
}

符合规范版:(很多实现都是为了符合规范)

const PENDING = 'pending';
const RESOLVED = 'resolved';
const REJECTED = 'rejected';

function MyPromise(fn) {
  // 代码可能异步,方便获取正确的this
  const that = this;
  this.state = PENDING;
  this.value = null
  // 保存then中的回调,当构造函数执行完后,Promise可能还在等待中,然后把then回调保存起来,当状态改变后, 所有的resolve回调将以注册时的顺序执行
  this.resolvedCallbacks = []
  this.rejectedCallbacks = []

  function resolve(value) {
    // 判断是否是Promise类型
    if (value instanceof MyPromise) {
      return value.then(resolve, reject);
    }

    setTimeout(() => {
      // 状态只能变更一次,变完后不可再改变
      if (that.satte === PENDING) {
        that.state = RESOLVED
        // 必须有一个value值,且不可改变
        that.value = value
        // 遍历回调数组并执行
        that.resolvedCallbacks.map(cb => cb(that.value));
      }
    }, 0)
  }
   
  function reject(value) {
    setTimeout(() => {
      if (that.satte === PENDING) {
        that.state = REJECTED
        that.value = value
        that.rejectedCallbacks.map(cb => cb(that.value))
      }
    }, 0)
  }

  // 执行构造函数
  try {
    fn(resolve, rejected)
  } catch (e) {
    reject(e)
  }
}


MyPromise.prototype.then = function(onFulfilled, onRejected) {
  const that = this;
  // 两个参数是可选参数,所以判断一下,当参数不是函数类型的时候,也需要创建一个函数赋值给对应的参数,实现透传
  // 如果onFulfilled和onRejected不是一个函数,promise2一定会接受到一样的value或reason
  onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value
  onRejected = typeof onRejected === 'function' 
      ? onRejected 
      : r => {
        throw r
      }
  if (that.state === PENDING) {
    return (promise2 = new MyPromise((resolve, reject) => {
      that.resolvedCallbacks.push(() => {
        try {
          const x = onFulfilled(that.value)
          resolutionProcedure(promise2, x, resolve, reject)
        } catch (r) {
          reject(r)
        }
      })

      that.rejectedCallbacks.push(() => {
        try {
          const x = onRejected(that.value)
          resolutionProcedure(promise2, x, resolve, reject)
        } catch (r) {
          reject(r)
        }
      })
    }))
  }
  if (that.state === RESOLVED) {
    // then方法一定会返回一个promise
    return (promise2 = new MyPromise((resolve, reject) => {
      // onFulfilled和onRejected只在构造函数执行完后调用
      setTimeout(() => {
        // 如果onFulfilled或者onRejected发生异常,一定捕获异常
        try {
          // onFulfilled和onRejected作为函数形式调用,所以this指向global
          const x = onFulfilled(that.value)
          // 如果 onFulfilled 或 onRejected 返回的是一个 x,那么它会以[[Resolve]](promise2, x) 处理解析
          resolutionProcedure(promise2, x, resolve, reject)
        } catch (r) {
          reject(r)
        }
      })
    }))
    onFulfilled(that.value)
  }
  if (this.satte === REJECTED) {
    return (promise2 = new MyPromise((resolve, reject) => {
      setTimeout(() => {
        try {
          const x = onRejected(that.value)
          resolutionProcedure(promise2, x, resolve, reject)
        } catch (r) {
          reject(r)
        }
      })
    }))
  }

  function resolutionProcedure(promise2, x, resolve, reject) {

    // 如果返回的 promise2 和 x 是指向同一个引用(循环引用),则抛出错误
    if (promise2 === x) {
      return reject(new TypeError('Error'))
    }

    // 如果 x 是一个 promise 实例,则采用它的状态:
    // 如果 x 是 pending 状态,那么保留它(递归执行这个 promise 处理程序),直到 pending 状态转为 fulfilled 或 rejected 状态
    if (x instanceof MyPromise) {
      x.then(function(value) {
        resolutionProcedure(promise2, value, resolve, reject)
      }, reject)
    }
    
    let called = false;
    if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
      // 尝试生成一个promise处理x,否则直接resolve
      try {
        let then = x.then
        if (typeof then === 'function') {
          then.call(
            x,
            y => {
              if (called) return
              called = true
              resolutionProcedure(promise2, y, resolve, reject)
            },
            e => {
              if (called) return
              called = true
              reject(e)
            }
          )
        } else {
          resolve(x)
        }
      } catch(e) {
        if (called) return
        called = true
        reject(e)
      }
    } else {
      resolve(x)
    }
  }
}

译:Promise/A+规范
实现一个简单promise

Generator

Generator的思想比较像Thunk,每次执行后将结果以函数形式返回,然后next其实就是再次执行。Generator配上一个自动执行函数便可以将异步代码像同步一样。
image

babel这个代码简化后得到的实现

function* helloWorldGenerator() {
  yield 'hello';
  yield 'world';
  return 'ending';
}

实现

(function() {
  var ContinueSentinel = {};
  // 构建Generator原型关系,判断关系时function*实例的__proto__指向Generator,像上面那张图
  var mark = function(genFun) {
    var generator = Object.create({
      next: function(arg) {
        return this._invoke("next", arg);
      }
    });
    genFun.prototype = generator;
    return genFun;
  };

  // 执行function*其实就是执行这个,返回一个generator,这个的原型就是outerFn.prototype,而这个又是mark函数返回的genFun.prototype
  function wrap(innerFn, outerFn, self) {
    var generator = Object.create(outerFn.prototype);
    
    // 一个全局对象
    var context = {
      done: false,
      method: "next",
      next: 0,    // 每次next时修改
      prev: 0,    // 每次next时修改
      // 当在generator中return的时候执行,此时则会执行中断整个generator
      abrupt: function(type, arg) {
        var record = {};
        record.type = type;
        record.arg = arg;

        return this.complete(record);
      },
      // 然后在执行到这
      complete: function(record, afterLoc) {
        if (record.type === "return") {
          this.rval = this.arg = record.arg;
          this.method = "return";
          this.next = "end";
        }

        // 是个空对象
        return ContinueSentinel;
      },
      // 由于complete中将next置为end,则执行stop函数
      stop: function() {
        this.done = true;
        return this.rval;
      }
    };

    generator._invoke = makeInvokeMethod(innerFn, context);

    return generator;
  }

  // next方法
  function makeInvokeMethod(innerFn, context) {
    var state = "start";

    return function invoke(method, arg) {
      if (state === "completed") {
        return { value: undefined, done: true };
      }

      context.method = method;
      context.arg = arg;

      while (true) {
        state = "executing";

        // 记录每次next执行的值
        var record = {
          type: "normal",
          arg: innerFn.call(self, context)   // 执行yield后的表达式
        };

        if (record.type === "normal") {
          state = context.done ? "completed" : "yield";

          // 第三次执行时,由于abrupt返回了个空对象,所以这里相等,则继续执行到第四步,返回了{value: ending, done: true}
          if (record.arg === ContinueSentinel) {
            continue;
          }

          return {
            value: record.arg,
            done: context.done
          };
        }
      }
    };
  }

  window.regeneratorRuntime = {};

  regeneratorRuntime.wrap = wrap;
  regeneratorRuntime.mark = mark;
})();

var _marked = regeneratorRuntime.mark(helloWorldGenerator);

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

          case 2:
            _context.next = 4;
            return "world";

          case 4:
            // 中断代码
            return _context.abrupt("return", "ending");

          case 5:
          case "end":
            return _context.stop();
        }
      }
    },
    _marked,
    this
  );
}

var hw = helloWorldGenerator();

console.log(hw.next());
console.log(hw.next());
console.log(hw.next());
console.log(hw.next());

Async

async其实是Generator的语法糖。

优点:

  • 内置执行器,自带执行器
  • 更好的语义
  • 适用性更广泛,co库约定,yield后面只能跟Thunk函数和Promise对象,而await后面可以是Promise或者原始类型
  • 可以捕获异步和同步的报错
  • 同步调试代码

实现并发

1、Promise.all
2、

async function loadData(urls) {
  // 并发读取 url
  const textPromises = urls.map(async url => {
    const response = await fetch(url);
    return response.text()
  })
  // 按次序输出
  for (const textPromise of textPromises) {
    console.log(await textPromise);
  }
}

实现

(function() {
    var ContinueSentinel = {};

    var mark = function(genFun) {
        var generator = Object.create({
            next: function(arg) {
                return this._invoke("next", arg);
            }
        });
        genFun.prototype = generator;
        return genFun;
    };

    function wrap(innerFn, outerFn, self) {
        var generator = Object.create(outerFn.prototype);

        var context = {
            done: false,
            method: "next",
            next: 0,
            prev: 0,
            sent: undefined,
            abrupt: function(type, arg) {
                var record = {};
                record.type = type;
                record.arg = arg;

                return this.complete(record);
            },
            complete: function(record, afterLoc) {
                if (record.type === "return") {
                    this.rval = this.arg = record.arg;
                    this.method = "return";
                    this.next = "end";
                }

                return ContinueSentinel;
            },
            stop: function() {
                this.done = true;
                return this.rval;
            }
        };

        generator._invoke = makeInvokeMethod(innerFn, context);

        return generator;
    }

    function makeInvokeMethod(innerFn, context) {
        var state = "start";

        return function invoke(method, arg) {
            if (state === "completed") {
                return { value: undefined, done: true };
            }

            context.method = method;
            context.arg = arg;

            while (true) {
                state = "executing";

                if (context.method === "next") {
                    context.sent = context._sent = context.arg;
                }

                var record = {
                    type: "normal",
                    arg: innerFn.call(self, context)
                };

                if (record.type === "normal") {
                    state = context.done ? "completed" : "yield";

                    if (record.arg === ContinueSentinel) {
                        continue;
                    }

                    return {
                        value: record.arg,
                        done: context.done
                    };
                }
            }
        };
    }

    window.regeneratorRuntime = {};

    regeneratorRuntime.wrap = wrap;
    regeneratorRuntime.mark = mark;
})();

"use strict";

// 这里就是自动执行其实思路类似前面实现的co库
function _asyncToGenerator(fn) {
    return function() {
        // 相当于result = fn.next(data)
        var gen = fn.apply(this, arguments);
        return new Promise(function(resolve, reject) {
            function step(key, arg) {
                try {
                    var info = gen[key](arg);
                    var value = info.value;
                } catch (error) {
                    reject(error);
                    return;
                }
                if (info.done) {
                    resolve(value);
                } else {
                    // 没有执行完则继续通过Promise递归调用
                    return Promise.resolve(value).then(
                        function(value) {
                            step("next", value);
                        },
                        function(err) {
                            step("throw", err);
                        }
                    );
                }
            }
            return step("next");
        });
    };
}

var fetchData = function fetchData(data) {
    return new Promise(function(resolve) {
        return setTimeout(resolve, 1000, data + 1);
    });
};

var fetchValue = (function() {
    var _ref = _asyncToGenerator(
        /*#__PURE__*/
        regeneratorRuntime.mark(function _callee() {
            var value1, value2, value3;
            return regeneratorRuntime.wrap(
                function _callee$(_context) {
                    while (1) {
                        switch ((_context.prev = _context.next)) {
                            case 0:
                                _context.next = 2;
                                return fetchData(1);

                            case 2:
                                value1 = _context.sent;
                                _context.next = 5;
                                return fetchData(value1);

                            case 5:
                                value2 = _context.sent;
                                _context.next = 8;
                                return fetchData(value2);

                            case 8:
                                value3 = _context.sent;

                                console.log(value3);

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

    return function fetchValue() {
        return _ref.apply(this, arguments);
    };
})();

fetchValue();

es相关源码
相关源码
深入理解Generator

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant