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

D.Promise + Generator #4

Open
Akiq2016 opened this issue Apr 23, 2017 · 0 comments
Open

D.Promise + Generator #4

Akiq2016 opened this issue Apr 23, 2017 · 0 comments

Comments

@Akiq2016
Copy link
Owner

Akiq2016 commented Apr 23, 2017

对于Generator,个人感觉至少要先掌握两个点:
一是Generator的“惰性”的特点,是一种暂停执行的函数。
二是next方法可以带参数,参数作为上一yield的返回值。

如果完全不理解,建议先看Generator的基本语法,这里只给两个例子,简单的过一遍这两个点。

// Generator的执行不是自动的,他需要调用next方法,来推动执行

function* g1 () {
  yield 'hello'
  yield 'world'
  return 'ending'
}
var test1 = g1() // 调用g1,返回的不是函数运行结果,而是一个指向内部状态的指针对象
test1.next() // Object {value: "hello", done: false}
test1.next() // Object {value: "world", done: false}
test1.next() // Object {value: "ending", done: true} 到这一步done的值为true,已经执行完了
test1.next() // Object {value: undefined, done: true} 再怎么执行 都是返回这个
// next方法带参数 和 不带参数

function* g2(x) {
  var y = 2 * (yield (x + 1));
  var z = yield (y / 3);
  return (x + y + z);
}

var test2 = g2(5)
test2.next() 
// 步骤 (x + 1) => (6 , 未执行完)
// 结果 Object{value: 6, done: false}

test2.next() 
// 步骤 (y = 2 * undefined) => (y / 3) => (NaN, 未执行完)
// 结果 Object{value:NaN, done: false}

test2.next() 
// 步骤 (z = undefined) => (5 + NaN + undefined) => (NaN, 执行完)
// 结果 Object{value:NaN, done: true}

var test3 = g2(5)
test3.next()
// 步骤 (x + 1) => (6 , 未执行完)
// 结果 Object{value: 6, done: false}

test3.next(12)
// 步骤 (y = 2 * 12) => (y / 3) => (8, 未执行完)
// 结果 Object{value:8, done: false}

test3.next(13)
// 步骤 (z = 13) => (5 + 24 + 13) => (42, 执行完)
// 结果 Object{value:42, done: true}

好的,Generator太懒了,要手动next才能一步步执行,很麻烦,那么我们让他自动执行。

var step1 = () => 100
var step2 = val => val + 2
var step3 = val => val + 3
var step4 = val => val + 4

function* g3() {
  try {
    var val1 = yield step1()
    var val2 = yield step2(val1)
    var val3 = yield step3(val2)
    var val4 = yield step4(val3)
    // Do something with val4
    console.log(val4)
  } catch (e) {
    // Handle any error from step1 through step4
  }
}

// 自动执行
var test4 = g3()
var res = test4.next()
while(!res.done){
  res = test4.next(res.value)
}

可以看的出,我们的g3的代码非常的清晰:想要获取step1的值,作为参数给step2,以此类推,依赖于val1, val2, val3 得出val4,并对val4进行后续操作。

那么,我们想把g3这种清晰酷炫的写法运用到异步上,【让异步写起来像同步】可以吗?然而必须保证前一步执行完,才能执行后一步的话,上面这个自动执行部分的写法就不适用于异步操作。

先引用个阮老师es6教程给的例子

const fetch = require('node-fetch')

// 这段代码非常像同步操作,除了加上了yield命令。
function* g4(){
  let url = 'https://api.github.com/users/github'
  let result = yield fetch(url)
  console.log(result.bio)
}

// 执行上述代码的方法,让generator自动跑起来。
var test5 = g4()
var result = test5.next()
result.value
  .then((data) => data.json())
  .then((data) => {test5.next(data)})

虽然 Generator 函数将异步操作表示得很简洁,但是流程管理却不方便(即何时执行第一阶段、何时执行第二阶段)。

但是我们却可以把流程这部分的操作总结出规律:当获取到该步的值val1时,则调用next并把val1传递;当获取到下一步的值val2时,则调用next并把val2传递。这是个始终在获取到值后,再进行下一步操作的过程。因此

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(g4)

下一篇

E.Async function

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