javascript - 如何与异步 JavaScript 生成器进行双向通信?

标签 javascript typescript functional-programming generator

与 js 生成器进行 2 路通信的能力非常强大(参见 here )。它支持类似于基于 CSP 的编程语言中的 channel 的功能。

不过,我似乎不知道如何使用异步生成器来做到这一点。假设我通过以下方式创建一个异步生成器:

async function* asyncGenFn() {
  yield Promise.resolve(true)
}

该函数返回AsyncIterableIterator (不是 AsyncIterator ),它似乎没有 next功能类似于 IterablIterator确实如此。

有没有办法与以这种方式创建的异步生成器进行双向通信?或者我只是在这里咆哮错误的树?

最佳答案

这是使用生成器进行双向通信的示例 -

const g = function* (x)
{ // yield sends a message "out"
  // inbound message will be assigned to msg1
  const msg1 = yield "sending this out"
  
  // another outbound message
  // inbound message will be assigned to msg2
  const msg2 = yield "sending this too" 
  
  // do something
  console.log(msg1, msg2)
  
  // finally, return a value
  // don't forget generators can accept arguments, like x here
  return x * 2
}

// instantiate the generator
const iterator = g (100)

// get the first value from the iterator
let val = iterator.next()

// some example message to send to the generator
let m = 1

while (!val.done)
{ // log each outbound message
  console.log("received message", val.value)
  
  // .next resumes the generator and sends a message back "in"
  val = iterator.next(m)
  
  // increment the example message
  m = m + 1
}

// display the final value
console.log("return value", val.value)

输出

received message sending this out
received message sending this too
1 2
return value 200
<小时/>

通过将其应用于问题来了解双向通信可能会更好。生成器为我们提供的这种暂停/恢复行为使它们非常适合处理异步操作。较新的 asyncawait 使我们能够模糊同步和异步代码之间的界限 -

const delay = x =>
  new Promise (r => setTimeout (r, 1e3, x))

const main = async (z) =>
{ const x = await delay (200) // some promise
  const y = await delay (300) // some promise
  return x + y + z            // some computation with all the values
}

main (100) .then (console.log, console.error)
// 2 seconds later...
// => 600

但在我们拥有 asyncawait 之前,我们有生成器。下面的 run 是与生成器进行双向通信的一个很好的演示。它允许我们以完全相同的方式编写程序,除了使用生成器函数和 yield 表达式 -

const delay = x =>
  new Promise (r => setTimeout (r, 1e3, x))

const main = function* (z)
{ const x = yield delay (200) // some promise
  const y = yield delay (300) // some promise
  return x + y + z            // some computation with all the values
}

const run = it =>
{ const loop = ({ done, value }) =>
    done
      ? Promise .resolve (value)
      : value .then (x => loop (it .next (x)))
  return loop (it .next ())
}

run (main (100)) .then (console.log, console.error)
// 2 seconds later...
// => 600

上面,run 被实现为一个简单的递归函数,它接受生成器的出站 promise 并将 promise 的解析值发送回生成器。它会执行此操作,直到生成器耗尽并解析出最终值 -

const run = it =>
{ const loop = ({ done, value }) =>

    // if the iterator is done
    done

      // resolve the final value
      ? Promise .resolve (value)

      // otherwise resolve the value, send it back into the generator, recur
      : value .then (x => loop (it .next (x)))

  // initialize the loop with the first value
  return loop (it .next ())
}

在我们可以使用生成器来模拟协程之前,我们一直在通过手动链接代码中的 .then 调用来编写异步程序 -

const delay = x =>
  new Promise (r => setTimeout (r, 1e3, x))

const main = z =>
  delay (200) .then (x => // manually chain then
  delay (300) .then (y => // manually chain then
  x + y + z               // some computation with all the values
  ))                      // close each then
  
main (100) .then (console.log, console.error)
// 2 seconds later...
// => 600

正如您所看到的,与生成器的双向通信非常强大,为我们提供了复杂程序的精美表达。 JavaScript 添加了 async/await 关键字,这可能看起来很神奇,但 run 让您了解如何使用双向通信即使没有新关键字,也能获得相同的行为。

关于javascript - 如何与异步 JavaScript 生成器进行双向通信?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54224476/

相关文章:

scala - 如何在 Scala 中使类完全不可变

javascript - Service Worker 检测缓存已完成

javascript - 如何让这个箭头不断旋转

angular - 将 Typescript 类型同步到 Firebase Object Observables 而不会丢失功能等

typescript - rxjs 中的纯函数

javascript - 有哪些学习 JavaScript 和编程架构的好网站?

javascript - 如何使用javascript实现悬停效果?

javascript - lodash zip 函数可以处理数组数组吗?

angular - 可观察<数组> Angular2

recursion - 使用 2 个递归调用进行 F# 尾调用优化?