使用redux-saga时,为什么yield语句是执行的值而不是undefined?

sun*_*uan 1 javascript ecmascript-6 redux redux-saga

假设我们有以下生成器函数:

function* testGenerator() {

  const result = yield Promise.resolve('foobar').then(res => res);

  console.log(result);

}

如果我使用以下行运行此生成器,它会记录 undefined

const test = testGenerator();
test.next();
test.next();
Run Code Online (Sandbox Code Playgroud)

但在传奇中,这样的线路会记录下来foobar.我只是好奇这背后的机制是什么(将yield的结果赋给变量)

编辑:

TL; DR:

MDN功能*参考

使用参数调用next()方法将恢复生成器函数执行,替换使用next()中的参数暂停执行的yield语句

基本上,要使其记录"foobar",只需将第一个yield的值传递给第二个yield,这样当它恢复时,yield语句将替换为值:

function* testGenerator() {
  const result = yield Promise.resolve('foobar');
  console.log(result);
}

const test = testGenerator();
test.next().value.then(r => test.next(r))
Run Code Online (Sandbox Code Playgroud)

Tha*_*you 5

代码审查

首先,摆脱这个; 它没有做任何事情

const result = yield Promise.resolve('foobar').then(res => res);
Run Code Online (Sandbox Code Playgroud)

你的发电机工作正常

如果我使用以下行运行此生成器,它将记录未定义

不,不.它{next, value}每次都会记录一次

function* testGenerator() {
  const result = yield Promise.resolve('foobar');
  console.log(result);
}

let test = testGenerator()

console.log(test.next())
// { value: Promise {[[PromiseStatus]]: "resolved", [[PromiseValue]]: 1}, done: false }

console.log(test.next())
// { value: undefined, done: true }
Run Code Online (Sandbox Code Playgroud)

记录承诺是棘手的.如果你不相信我,请看这个

function* testGenerator() {
  const result = yield Promise.resolve('foobar');
  console.log(result);
}

let test = testGenerator()

test.next().value.then(console.log)
// "foobar"
Run Code Online (Sandbox Code Playgroud)


协同程序

但是在传奇中,这样的线条会记录foobar.我只是好奇这背后的机制是什么(将yield的结果赋给变量)

Redux Saga可以通过发电机进行操作并处理各种效果.承诺只是你可以屈服的众多事情之一.

这是一个简单的协同程序函数的示例,该函数接受生成器实例并期望在最后一个结果之前产生Promises.注意双向数据流.

  • yield 从发电机发送数据
  • gen.next(x) 将下一个值发送到生成器

因此,我们正在利用这种能力的发电机送承诺出去,并发送承诺的决心回值,这使得解决承诺的价值将被直接分配给一个变量

const coro = gen => {
  const next = x => {
    const {value, done} = gen.next(x)
    if (done)
      return value
    else
      return value.then(next)
  }
  return next()
}

function* testGenerator () {
  const x = yield Promise.resolve(1)
  console.log(x) // 1

  const y = yield Promise.resolve(2)
  console.log(y) // 2

  const z = yield Promise.resolve(3)
  console.log(z) // 3

  return x + y + z
}

coro(testGenerator()).then(console.log)
// "6"
Run Code Online (Sandbox Code Playgroud)


async & await

现在您已了解这一点,您完全理解提议async/ await工作的功能 - 下一个代码段的功能可能会因浏览器的支持而有所不同.

变化:

  • 我们不再需要coro帮手
  • 使用async关键字而不是生成器function*
  • 使用await关键字代替yield

行为是一样的.

  • 使用await发送出去的承诺
  • Promise的解析值在发回的返回值await调用
  • async函数隐式返回一个Promise,因此链接一个.then调用以获取最终的值

const testRoutine = async () => {
  const x = await Promise.resolve(1)
  console.log(x) // 1

  const y = await Promise.resolve(2)
  console.log(y) // 2

  const z = await Promise.resolve(3)
  console.log(z) // 3

  return x + y + z
}

testRoutine().then(console.log)
// "6"
Run Code Online (Sandbox Code Playgroud)


本土协同程序

你不应该推出自己的协同程序,因为有很多东西需要注意.例如,如果生成器throw是一个Error或yieldsa拒绝了Promise,那么上面的实现只会吞下它.

为了演示目的,我将向您展示如何更多地说明它,但如果您有兴趣覆盖所有基础,您应该阅读Redux Saga源代码,或者像tj这样的另一个协程库的源代码/ CO

const coro = gen => {
  return new Promise((resolve, reject) => {
    const next = x => {
      try {
        let {value, done} = gen.next(x)
        return done ? resolve(value) : value.then(next, reject)
      }
      catch (err) {
        reject(err)
      }
    }
    next()
  })
}

function* noYield() { return 5 }

function* throwsUp() { throw Error("OOPS") }

function* yieldReject() { yield Promise.reject('NO') }

function* normal() { let x = yield Promise.resolve(16); return x * x; }

coro(noYield())
  .then(console.log, console.error) // 5

coro(throwsUp())
  .then(console.log, console.error) // [Error: OOPS]
  
coro(yieldReject())
  .then(console.log, console.error) // "NO"
  
coro(normal())
  .then(console.log, console.error) // 256
Run Code Online (Sandbox Code Playgroud)