【问题标题】:Implementing event synchronization primitives in JavaScript/TypeScript using async/await/Promises使用 async/await/Promises 在 JavaScript/TypeScript 中实现事件同步原语
【发布时间】:2018-04-19 17:19:53
【问题描述】:

我在 TypeScript/JavaScript 中有一个漫长而复杂的异步过程,分布在许多库和函数中,当它最终完成接收和处理所有数据时,调用一个函数 processComplete() 来表示它已经完成:

processComplete(); // Let the program know we're done processing

现在,该函数看起来像这样:

let complete = false;

function processComplete() {
   complete = true;
}

为了确定进程是否完成,其他代码要么使用超时,要么使用process.nextTick 并在循环中一遍又一遍地检查complete 变量。这既复杂又低效。

我想让各种async 函数简单地使用await 等待并在过程完成时被唤醒:

// This code will appear in many different places
await /* something regarding completion */;
console.log("We're done!");

如果我在 Windows 中使用 C 进行编程,我会使用 event synchronization primitive,代码看起来像这样:

Event complete;

void processComplete() {
   SetEvent(complete);
}

// Elsewhere, repeated in many different places
WaitForSingleObject(complete, INFINITE);
console.log("We're done!");

在 JavaScript 或 TypeScript 中,除了将布尔值 complete 设置为 true,processComplete 究竟可以做什么来唤醒正在使用 await 等待的任意数量的函数?换句话说,如何使用awaitasyncPromises 实现事件同步原语?

【问题讨论】:

  • 承诺就是这样。只需解决一个承诺并await 它。
  • @SLaks 我无法弄清楚如何提前创建承诺,以便事情可以等待它,然后在以后调用 processComplete 时解决它。
  • 您能否编辑您的问题以包含触发进程启动的代码和调用processComplete 的代码?
  • @JohnSpeeks — 哦,Angular 有一个成熟的事件系统。您可以传递事件发射器或 observable 并根据需要订阅它,而不是传递 complete 变量。 angular.io/api/core/EventEmitter
  • 只需将 complete = true; 替换为 resolve() 并将整个内容包装成一个巨大的 Promise。或者花点时间将它全部重构为基于 Promises。

标签: javascript typescript promise async-await


【解决方案1】:

此模式与您的代码非常接近:

const processComplete = args => new Promise(resolve => {
   // ...
   // In the middle of a callback for a async function, etc.: 
     resolve(); // instead of `complete = true;`
   // ...
}));

// elsewhere
await processComplete(args); 
console.log("We're done!");

更多信息:https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise

【讨论】:

  • 这看起来会立即解决承诺,而无需等待任何异步实际完成
  • 我相信 OP 的例子,processComplete 真的类似于resolve 函数。这不是异步任务。
  • @KirillBulygin 我不确定这是否正确。现有代码在一切完成后调用processComplete() 函数,但该函数似乎不再存在。在这种情况下,可能会有 5 或 10 个不同的函数在等待该过程完成。
  • @JohnSpeeks 我看到您在我回复后添加了“此代码将出现在许多不同的地方”。我认为诺言不适合你。尝试 cmets 中建议的事件系统。
  • @KirillBulygin 我之前提到过它会出现在文本中的各个地方,但也添加了该评论以使其更加清晰。我仍然觉得使用Promiseawait 应该可以做到这一点,但我只是不确定如何——必须有某种方法来弥合差距。
【解决方案2】:

在这种情况下,这实际上取决于您所说的“其他代码”是什么意思。听起来您想使用delegation patternobserver pattern 的一些变体。

一种简单的方法是利用 JavaScript 允许您存储函数数组这一事实。您的 processComplete() 方法可以执行以下操作:

function processComplete(){
    arrayOfFunctions.forEach(fn => fn());
}

在其他地方,在您的其他代码中,您可以为流程完成后需要执行的操作创建函数,并将这些函数添加到arrayOfFunctions

如果您不希望这些不同的代码部分如此紧密地联系在一起,您可以设置一个完全独立的代码部分,用作通知中心。然后,您将让您的其他代码告诉通知中心它希望在该过程完成时得到通知,而您的 processComplete() 方法将简单地告诉通知中心该过程已完成。

另一种方法是use promises

【讨论】:

    【解决方案3】:

    我在 TypeScript/JavaScript 中有一个漫长而复杂的异步过程,分布在许多库和函数中

    然后确保异步过程的每一位都为其部分结果返回一个承诺,以便您可以链接它们并将它们组合在一起或await它们。

    当它最终完成接收和处理所有数据时,调用函数processComplete() 表示它已完成

    不应该。启动进程的函数应该返回一个承诺,当进程完成时,它应该履行这个承诺。

    如果你不想正确地承诺整个过程的每一点,因为它太麻烦了,你可以这样做

    function startProcess(…);
        … // do whatever you need to do
        return new Promise(resolve => {
            processComplete = resolve;
            // don't forget to reject when the process failed!
        });
    }
    

    在 JavaScript 或 TypeScript 中,除了将布尔值 complete 设置为 true,processComplete 究竟可以做什么来唤醒正在使用 await 等待的任意数量的函数?

    如果他们已经 awaiting 承诺的结果,则不需要做任何其他事情。 (等待的承诺内部已经有这样的标志)。真的只是在做

    // somewhere:
    var resultPromise = startProcess(…);
    

    // elsewhere:
    await resultPromise;
    … // the process is completed here
    

    如果您只需要同步您的任务,您甚至不需要用有用的结果来履行承诺,但您确实应该这样做。 (如果没有他们在等待的数据,他们到底在等什么?)

    【讨论】:

      猜你喜欢
      • 2019-03-06
      • 2019-07-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-01-08
      • 1970-01-01
      • 2021-07-21
      • 1970-01-01
      相关资源
      最近更新 更多