美文网首页异步处理的原理async
async await 实现原理 generator + yie

async await 实现原理 generator + yie

作者: 芗芗_ | 来源:发表于2018-08-22 16:04 被阅读0次

    generator函数

    • Generator 函数整体就是一个状态机,内部有多少个yield,就有多少个状态,当Generator函数执行的时候 其实是返回包含每次代码暂停执行的指针对象(遍历器对象Iterator),可以遍历 Generator的各种状态(Iterator.next 去移动指针可以使 Generator 函数分段执行)

    • {value:xxx,done:false || true},表示这个状态的返回值和一个表示这个遍历对象是否已经遍历到最后(状态机是否走完了所有的状态)

    Iterator接口

    typescript的接口描述如下:遍历器接口(Iterable)、指针对象(Iterator)和next方法返回值

    interface Iterable {
      [Symbol.iterator]() : Iterator,
    }
    
    interface Iterator {
      next(value?: any) : IterationResult,
    }
    
    interface IterationResult {
      value: any,
      done: boolean,
    }
    
    • 一种数据结构只要部署了 Iterator 接口,我们就称这种数据结构是“可遍历的”(iterable)。

    • ES6 规定,默认的 Iterator 接口部署在数据结构的Symbol.iterator属性,或者说,一个数据结构只要具有Symbol.iterator属性,就可以认为是“可遍历的”(iterable)

    -ES6 的有些数据结构原生具备 Iterator 接口(比如数组),即不用任何处理,就可以被for...of循环遍历。原因在于,这些数据结构原生部署了Symbol.iterator属性(详见下文),另外一些数据结构没有(比如对象)。凡是部署了Symbol.iterator属性的数据结构,就称为部署了遍历器接口。调用这个接口,就会返回一个遍历器对象

    调用 Iterator 接口的场合

    • (1)解构赋值 对数组和 Set 结构进行解构赋值时,会默认调用Symbol.iterator方法。
    • (2)扩展运算符 扩展运算符(...)也会调用默认的 Iterator 接口。
    • (3)yield* yield*后面跟的是一个可遍历的结构,它会调用该结构的遍历器接口。
    let generator = function* () {
      yield 1;
      yield* [2,3,4];
      yield 5;
    };
    
    var iterator = generator();
    
    iterator.next() // { value: 1, done: false }
    iterator.next() // { value: 2, done: false }
    iterator.next() // { value: 3, done: false }
    iterator.next() // { value: 4, done: false }
    iterator.next() // { value: 5, done: false }
    iterator.next() // { value: undefined, done: true }
    

    for...in循环有几个缺点。

    数组的键名是数字,但是for...in循环是以字符串作为键名“0”、“1”、“2”等等。
    for...in循环不仅遍历数字键名,还会遍历手动添加的其他键,甚至包括原型链上的键。
    某些情况下,for...in循环会以任意顺序遍历键名。
    总之,for...in循环主要是为遍历对象而设计的,不适用于遍历数组。
    看了好几遍阮一峰的es6语法才看懂
    Generator 函数返回一个遍历器 这个遍历器提供 next() 方法来控制Generator函数的执行,暂停 。
    yield 表达式 暂停的标志

    简易版本的Generator函数遍历器自执行函数:

    // 协程 callback版本
    var timeout = (time)=>{
      return (callbck)=>{
           setTimeout(()=>{
          console.log('time',time)
          callbck()
        },time)
      }
    }
    
    
    
    // Generator 遍历器生成函数
    function* callbackGenerator() {
      var res = yield timeout(2000) // 使得 yield 的value 是一个callback 自执行递归 使下一次的next在callback里面执行
      var res1 = yield timeout(2000)
    }
    
    
    
    // callback版本  版本自执行函数
    function run (Generator) {
      var hw = Generator();
     const  next = ()=>{
       const aa = hw.next()
       if(!aa.done){
        aa.value(next) 
       }else{
         return true
       }
     }
     next()
    }
    
    
    
    run(callbackGenerator)
    
    
    // 协程 promise 版本
    var timeout = (time)=>{
      return new Promise((resolve)=>{
           setTimeout(()=>{
          console.log('time',time)
          resolve(time+2000)
        },time)
      })
    }
    
    
    
    // Generator 遍历起器生成函数
    function* callbackGenerator() {
      var res = yield timeout(2000) // 使得 yield 的value是promise 自执行时把下一次的next在then里面执行
      var res1 = yield timeout(res)
    }
    
    
    
    // promise版本 版本自执行函数
    function run (Generator) {
      var hw = Generator();
     const  next = (query)=>{
       var aa = hw.next(query)
       if(!aa.done){
        aa.value.then(next) 
       }else{
         return true
       }
     }
     next()
    }
    
    
    
    run(callbackGenerator)
    
    
    

    核心思想:利用generator遍历器生成器函数的分段执行 ,只有在遍历器对象 执行next方法之后交出了控制权 ,在完成后 callback || promise.then()里面调用下一次next的时候又继续恢复控制权这个功能来实现的

    async await 正式利用了这一点

    • yield 表达式交出控制权

    两种方法可以做到这一点。
    (1)回调函数。将异步操作包装成 Thunk 函数,在回调函数里面交回执行权。

    (2)Promise 对象。将异步操作包装成 Promise 对象,用then方法交回执行权。
    from http://es6.ruanyifeng.com/#docs/generator-async

    • 使generator遍历器函数自执行

    es7 async await 异步写法

    function sleep(ms) {
      return new Promise((resolve, reject) => {
        setTimeout(resolve, ms);
      });
    }
    
    async function test() {
      for (let i = 0; i < 10; i++) {
        await sleep(100);
      }
    }
    

    转化成es6是下边这样
    async await 实际上是由 generator + yield 控制流程 + promise 实现回调

    // _asyncToGenerator 顾名思义转换async to Generator

    • 转换 await 为 yield
    • 转换 async 为 Generator
    • 调用test之前 _asyncToGenerator函数 已经执行,并传递了一个fn (Generator)给匿名的执行函数
    • 实际上 调用test之前, test方法已经变成了一个 实现 Generator 遍历器生成函数自执行的 方法
    • 调用test 递归执行step
      • 调用next的来执行遍历器
      • 每次执行返回一个promise
      • promise的then方法里执行next方法)
    • 使得Generator 遍历器自动 执行, 从而达到异步
    function _asyncToGenerator(fn) {
      return function () {
        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 {
              return Promise.resolve(value).then(function (value) {
                return step("next", value);
              },
                function (err) {
                  return step("throw", err);
                });
            }
          }
          return step("next");
        });
      };
    }
    
    function sleep(ms) {
      return new Promise((resolve, reject) => {
        setTimeout(resolve, ms);
      });
    }
    
    let test = function () {
      var ref = _asyncToGenerator(function* () {
        for (let i = 0; i < 10; i++) {
          yield sleep(100);
        }
      });
    
      return function test() {
        return ref.apply(this, arguments);
      };
    }();
    

    相关文章

      网友评论

        本文标题:async await 实现原理 generator + yie

        本文链接:https://www.haomeiwen.com/subject/avkyiftx.html