Await dispatchEvent:当侦听器异步时如何同步侦听 dispatchEvent

Await dispatchEvent: How to listen synchronously to a dispatchEvent when listener is async

为了开发一个软件插件,我需要监听一个由 dispatchEvent 产生的事件,并在接收到这个事件时执行一些操作。重要的是我的动作应该在软件继续执行dispatchEvent.

之后的代码之前完成

因为dispatchEvent是同步的,所以应该是这样的。。。可惜我的动作是异步的(基本上需要等视频被求到),这样的话dispatchEvent 在我自己的代码结束之前继续。当被调用函数是异步的时,是否也可以获得 dispatchEvent 的同步行为?

示例:

我想要以下代码来生成输出:

Putting the clothes in the washing machine.
Machine started...
Machine ended...
Picking the washed clothes.

可惜洗衣机还没结束就把衣服挑了:

Putting the clothes in the washing machine.
Machine started...
Picking the washed clothes.
Machine ended...

我使用了这个代码:

var button = document.getElementById('button');
button.addEventListener('click', event => {
  console.log("Putting the clothes in the washing machine.");
  const machineResult = button.dispatchEvent(new CustomEvent('startmachine', {
    'detail': {
      timeProgram: 3000
    }
  }));
  if (machineResult.defaultPrevented) {
    console.log("Picking the dirty clothes.");
  } else {
    console.log("Picking the washed clothes.");
  }
});
button.addEventListener('startmachine', async event => {
  console.log("Machine started...");
  await new Promise(r => setTimeout(r, event.detail.timeProgram));
  console.log("Machine ended...");
});
<button id="button">Wash my clothes</button>

编辑:找到了一个我不喜欢的脏解决方案

显然,这似乎是不可能的,我使用了一种不同的方法,不幸的是 需要在代码 dispatchEvent 之后进行一些更改(因为我不负责这部分代码,我更喜欢在 dispatchEvent).

之后不更改代码的方法

想法是在事件中添加一个数组,每个侦听器都可以放置一个异步函数。然后,在 dispatchEvent 完成后,代码执行此数组中的所有函数,如果有 returns false 则认为事件已中止。

  var button = document.getElementById('button');
  button.addEventListener('click', async event => {
      console.log("Putting the clothes in the washing machine.");
      var listOfActions = [];
      const machineResult = button.dispatchEvent(new CustomEvent('startmachine', { 'detail': {timeProgram: 3000, listOfActions: listOfActions} }));
      results = await Promise.all(listOfActions.map(x => x()));
      if (machineResult.defaultPrevented || !results.every(x => x != false)) { // Do not use == true or boolean to ensure returning nothing does not abort.
          console.log("Picking the dirty clothes.");
      } else {
          console.log("Picking the washed clothes.");
      }
  });
  
  button.addEventListener('startmachine', event => {
      event.detail.listOfActions.push( async () => {
          console.log(">>> Machine started...");
          await new Promise(r => setTimeout(r, event.detail.timeProgram));
          console.log("<<< Machine ended...");
          // If you want to say that the clothes are dirty:
          // return false
      });
  });
<button id="button">Wash my clothes</button>

编辑 2

使用 promise 更好地处理错误:

        var button = document.getElementById('button');
  button.addEventListener('click', async event => {
      console.log("Putting the clothes in the washing machine.");
      var listOfActions = [];
      let cancelled = !button.dispatchEvent(new CustomEvent('startmachine', { 'detail': {timeProgram: 3000, listOfActions: listOfActions} }));
      results = await Promise.all(listOfActions);
      // Do not use == true or boolean to ensure returning nothing does not abort.
      cancelled = cancelled || !results.every(x => x != false)
      if (cancelled) {
          console.log("Picking the dirty clothes.");
      } else {
          console.log("Picking the washed clothes.");
      }
  });
  
  button.addEventListener('startmachine', event => {
      event.detail.listOfActions.push((async () => {
          console.log(">>> Machine started...");
          await new Promise(r => setTimeout(r, event.detail.timeProgram));
          console.log("<<< Machine ended...");
          // If you want to say that the clothes are dirty:
          //return false
      })());
      // ^-- The () is useful to push a promise and not an async function.
      // I could also have written let f = async () => { ... };  event....push(f())
  });
<button id="button">Wash my clothes</button>

Is it possible to also get a synchronous behavior for dispatchEvent when the called function is asynchronous?

不,不是。

The idea is to add an array in the event where each listener can put an async function. Then, after dispatchEvent is done, the code executes all functions in this array

是的,这是个好主意。然而,我会把它变成一个承诺数组,而不是一个函数数组。

Web 平台 API 在 ExtendableEvent 中使用相同的模式。不幸的是,该构造函数仅在服务工作者内部可用:-/