Generator与async/await与Generator的模拟
Generator
函数有多种理解角度。语法上,首先可以把它理解成,Generator 函数是一个状态机,封装了多个内部状态。
执行 Generator 函数会返回一个遍历器对象,也就是说,Generator 函数除了状态机,还是一个遍历器对象生成函数。返回的遍历器对象,可以依次遍历 Generator 函数内部的每一个状态。
形式上,Generator 函数是一个普通函数,但是有两个特征。一是,function关键字与函数名之间有一个星号;二是,函数体内部使用yield表达式,定义不同的内部状态(yield在英语里的意思就是“产出”)。
function* helloWorldGenerator() {
  yield 'hello';
  yield 'world';
  return 'ending';
}
var hw = helloWorldGenerator();
下一步,必须调用遍历器对象的next方法,使得指针移向下一个状态。也就是说,每次调用next方法,内部指针就从函数头部或上一次停下来的地方开始执行,直到遇到下一个yield表达式(或return语句)为止。换言之,Generator 函数是分段执行的,yield表达式是暂停执行的标记,而next方法可以恢复执行。
hw.next()
// { value: 'hello', done: false }
hw.next()
// { value: 'world', done: false }
hw.next()
// { value: 'ending', done: true }
hw.next()
// { value: undefined, done: true }
next 方法的参数
yield表达式本身没有返回值,或者说总是返回undefined。next方法可以带一个参数,该参数就会被当作上一个yield表达式的返回值。
function* foo(x) {
  var y = 2 * (yield (x + 1));
  var z = yield (y / 3);
  return (x + y + z);
}
var a = foo(5);
a.next() // Object{value:6, done:false}
a.next() // Object{value:NaN, done:false}
a.next() // Object{value:NaN, done:true}
var b = foo(5);
b.next() // { value:6, done:false }
b.next(12) // { value:8, done:false }
b.next(13) // { value:42, done:true }
for...of 循环
for...of循环可以自动遍历 Generator 函数时生成的Iterator对象,且此时不再需要调用next方法。
function* foo() {
  yield 1;
  yield 2;
  yield 3;
  yield 4;
  yield 5;
  return 6;
}
for (let v of foo()) {
  console.log(v);
}
基于 Promise 对象的自动执行
首先,把fs模块的readFile方法包装成一个 Promise 对象
var fs = require('fs');
var readFile = function (fileName){
  return new Promise(function (resolve, reject){
    fs.readFile(fileName, function(error, data){
      if (error) return reject(error);
      resolve(data);
    });
  });
};
var gen = function* (){
  var f1 = yield readFile('/etc/fstab');
  var f2 = yield readFile('/etc/shells');
  console.log(f1.toString());
  console.log(f2.toString());
};
然后,手动执行上面的 Generator 函数。
var g = gen();
g.next().value.then(function(data){
  g.next(data).value.then(function(data){
    g.next(data);
  });
});
手动执行其实就是用then方法,层层添加回调函数。理解了这一点,就可以写出一个自动执行器。一般使用模块。
function run(gen){
  var g = gen();
  function next(data){
    var result = g.next(data);
    if (result.done) return result.value;
    result.value.then(function(data){
      next(data);
    });
  }
  next();
}
run(gen);
上面代码中,只要 Generator 函数还没执行到最后一步,next函数就调用自身,以此实现自动执行。
async/await
async 函数是什么?一句话,它就是 Generator 函数的语法糖。
async函数对 Generator 函数的改进,体现在以下四点。
(1)内置执行器。
Generator 函数的执行必须靠执行器,所以才有了co模块,而async函数自带执行器。也就是说,async函数的执行,与普通函数一模一样,只要一行。
(2)更好的语义。
async和await,比起星号和yield,语义更清楚了。async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果。
(3)更广的适用性。
co模块约定,yield命令后面只能是 Thunk 函数或 Promise 对象,而async函数的await命令后面,可以是 Promise 对象和原始类型的值(数值、字符串和布尔值,但这时等同于同步操作)。
(4)返回值是 Promise。
async函数的返回值是 Promise 对象,这比 Generator 函数的返回值是 Iterator 对象方便多了。你可以用then方法指定下一步的操作。
async 函数的实现原理
async 函数的实现原理,就是将 Generator 函数和自动执行器,包装在一个函数里。
async function fn(args) {
  // ...
}
// 等同于
function fn(args) {
  return spawn(function* () {
    // ...
  });
}
所有的async函数都可以写成上面的第二种形式,其中的spawn函数就是自动执行器。
下面给出spawn函数的实现,基本就是前文自动执行器的翻版。
function spawn(genF) {
  return new Promise(function(resolve, reject) {
    const gen = genF();
    function step(nextF) {
      let next;
      try {
        next = nextF();
      } catch(e) {
        return reject(e);
      }
      if(next.done) {
        return resolve(next.value);
      }
      Promise.resolve(next.value).then(function(v) {
        step(function() { return gen.next(v); });
      }, function(e) {
        step(function() { return gen.throw(e); });
      });
    }
    step(function() { return gen.next(undefined); });
  });
}
模拟
生成器是通过暂停自己的作用域 / 状态实现它的“魔法”的。可以 通过函数闭包
- 1 是起始状态
- 2 是 request(..) 成功后的状态
- 3 是 request(..) 失败的状态
function foo(url) {
  var state;//管理状态
  var val;//生成器变量范围声明
  function process(v) {
    switch (state) {
      case 1:
        return request(url);
      case 2:
        val = v;
        return;
      case 3:
        var err = v;
        console.log( "Oops:", err );
        return false;
    }
 // 构造并返回一个生成器
 return {
    next: function(v) { // 初始状态
         if (!state) {
             state = 1;
             return {
                 done: false,
                 value: process()
             };
            }
        // yield成功恢复
        else if (state == 1) {
             state = 2;
             return {
                    done: true,
                    value: process( v )
                };
        }
        // 生成器已经完成
        else {
             return {
                 done: true,
                 value: undefined
             };
        } },
        "throw": function(e) {
        // 唯一的显式错误处理在状态1
        if (state == 1) {
             state = 3;
             return {
                 done: true,
                 value: process( e )
                 };
        }
            // 否则错误就不会处理,所以只把它抛回 else {
        throw e; }
    } };
}
这段代码是如何工作的呢?
- (1) 对迭代器的 next() 的第一个调用会把生成器从未初始化状态转移到状态 1,然后调用 process() 来处理这个状态。request(..) 的返回值是对应 Ajax 响应的 promise,作为 value 属性从 next() 调用返回。
- (2) 如果 Ajax 请求成功,第二个 next(..) 调用应该发送 Ajax 响应值进来,这会把状态转 移到状态 2。再次调用 process(..)(这次包括传入的 Ajax 响应值),从 next(..) 返回 的 value 属性将是 undefined。
- (3) 然而,如果 Ajax 请求失败的话,就会使用错误调用 throw(..),这会把状态从 1 转移到 3(而非 2)。再次调用 process(..),这一次包含错误值。这个 case 返回 false,被作 为 throw(..) 调用返回的 value 属性。
从外部来看(也就是说,只与迭代器交互),这个普通函数 foo(..) 与生成器 *foo(..) 的 工作几乎完全一样。所以我们已经成功地把 ES6 生成器转为了前 ES6 兼容代码!
然后就可以手工实例化生成器并控制它的迭代器了,调用var it = foo("..")和 it.next(..) 等。甚至更好的是,我们可以把它传给前面定义的工具 run(..),就像 run(foo,"..")。
regenerator 就 是 这 样 的 一 个 工 具(http://facebook.github.io/regenerator/), 出 自 Facebook 的 几个聪明人。
Generator与async/await与Generator的模拟的更多相关文章
- JS异步编程 (2) - Promise、Generator、async/await
		JS异步编程 (2) - Promise.Generator.async/await 上篇文章我们讲了下JS异步编程的相关知识,比如什么是异步,为什么要使用异步编程以及在浏览器中JS如何实现异步的.最 ... 
- ES Next & Arrow function & Promise & Iterator & Generator yield & Async Await
		ES Next & Arrow function & Promise & Iterator & Generator yield & Async Await co ... 
- Promise、Generator,Async/await
		我们知道JavaScript是单线程语言,如果没有异步编程非得卡死. 以前,异步编程的方法有下面四种 回调函数 事件监听 发布/订阅 Promise对象 现在据说异步编程终极解决方案是——async/ ... 
- async/await 与 generator、co 的对比
		之前写过一个分批预加载资源的插件,其实质便是串行执行异步,使用的方法是generator + promise -- 前几天写了一个爬虫,抓取页面的n个页面的音频资源,其也是串行执行异步,但是在使用的a ... 
- Promise与async/await与Generator
		Promise是什么: Promise是异步微任务(process.nextTick.Promise.then() catch() finally()等),用于解决异步多层嵌套回调的问题(回调地狱-- ... 
- Promise, Generator, async/await的渐进理解
		作为前端开发者的伙伴们,肯定对Promise,Generator,async/await非常熟悉不过了.Promise绝对是烂记于心,而async/await却让使大伙们感觉到爽(原来异步可以这么简单 ... 
- Promise、Generator、Async有什么区别?
		前言 我们知道Promise与Async/await函数都是用来解决JavaScript中的异步问题的,从最开始的回调函数处理异步,到Promise处理异步,到Generator处理异步,再到Asyn ... 
- [每日一题]面试官问:Async/Await 如何通过同步的方式实现异步?
		关注「松宝写代码」,精选好文,每日一题 时间永远是自己的 每分每秒也都是为自己的将来铺垫和增值 作者:saucxs | songEagle 一.前言 2020.12.23 日刚立的 flag,每日一 ... 
- 答应我,这次必须搞懂!痛点难点Promise。(小点心async/await,基于Promise的更优方案)
		Promise 出现的原因 在 Promise 出现以前,我们处理一个异步网络请求,大概是这样: // 请求 代表 一个异步网络调用. // 请求结果 代表网络请求的响应. 请求1(function( ... 
随机推荐
- centos 通用开发工具及库安装 有了它不用愁了
			通用开发工具及库:# yum groupinstall "Development Tools" "Development Libraries" 
- 2017.12.4 JavaWeb中EL表达式的运用
			<%@ page contentType="text/html; charset=gb2312"%> <html> <head> <tit ... 
- python_53_函数补充
			def test1(x,y=2): print(x,y) test1(1) test1(1,3) test1(1,y=4) #默认参数特点:调用函数的时候,默认参数非必须传递,默认参数放在后边 #用途 ... 
- 转:Python集合(set)类型的操作
			转自:http://blog.csdn.net/business122/article/details/7541486 python的set和其他语言类似, 是一个无序不重复元素集, 基本功能包括关系 ... 
- SpringBoot学习3:springboot整合filter
			整合方式一:通过注解扫描完成 Filter 组件的注册 1.编写filter package com.bjsxt.filter; import javax.servlet.*; import java ... 
- linux内核--定时器API
			/**<linux/timer.h> 定时器结构体 struct timer_list { ........ unsigned long expires; --内核希望定时器执行的jiff ... 
- pyhon之99乘法表
			1.长方形完整格式 for i in range(1,10): for j in range(1,10): print("%d*%d" %(j,i),end=" &quo ... 
- nginx修改nginx.conf配置可以https访问
			修改nginx.conf,参照如下更改配置server { listen 443; server_name abc.com; // 访问域名 ssl on; root /var/www/bjubi.c ... 
- LeetCode946-验证栈序列
			问题:验证栈序列 给定 pushed 和 popped 两个序列,只有当它们可能是在最初空栈上进行的推入 push 和弹出 pop 操作序列的结果时,返回 true:否则,返回 false . 示例 ... 
- DeepFaceLab进阶(4):通过Colab免费使用Tesla K80 跑模型!
			当学会了换脸软件DeepFaceLab基本使用,各种参数配置,各有优化技能之后.唯一约束你的可能是电脑配置. CPU能跑,但是慢到怀疑人生,低配模型都得跑一周 低配显卡,显存不够,H128 根本就跑不 ... 
