如何与异步JavaScript生成器进行2向通信?

时间:2019-01-16 20:00:39

标签: javascript typescript functional-programming generator

与js生成器进行2向通信的功能非常强大(请参阅here)。它启用了类似于基于CSP的编程语言中的通道的功能。

我似乎无法弄清楚如何使用异步生成器来做到这一点。假设我通过以下方式创建了一个异步生成器:

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

此函数返回AsyncIterableIterator(而不是AsyncIterator),它似乎不像next那样具有IterablIterator函数。

是否有一种方法可以与以此方式创建的异步生成器进行2通通信?还是我只是在这里树错树了?

2 个答案:

答案 0 :(得分:2)

这是使用发电机进行双向通讯的示例-

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

最好通过将2维通信应用于问题来学习2维通信。生成器赋予我们的这种暂停/恢复行为使它们非常适合处理异步操作。较新的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被实现为一个简单的递归函数,该函数接受生成器的出站承诺并将承诺的已解析值发送回生成器。它会一直执行到生成器用尽并解析出最终值-

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

如您所见,与生成器的2通通信功能强大,可为我们提供复杂程序的漂亮表达。 JavaScript添加了async / await关键字,这些关键字可能看起来像魔术,但是run使您了解了如何使用双向通信来获得相同的行为,即使没有新关键字。

答案 1 :(得分:1)

这是我最后一个与Typescript编译选项有关的问题。在我的def startClicking(): jsScript = open('clicks.js', 'r') jsScriptContent = jsScript.read() time.sleep(2) driver.execute_script(jsScriptContent) time.sleep(1) jsScript.close() 中添加以下内容为我解决了这个问题:

tsconfig.json

感谢大家参观!