【问题标题】:How to build an event generator in JavaScript如何在 JavaScript 中构建事件生成器
【发布时间】:2018-05-27 06:08:28
【问题描述】:

我正在尝试构建一种方法来创建可以产生 DOM 事件的生成器。更一般地说,我想创建一种将事件系统转换为产生事件的异步系统的方法。

我的初始代码示例有效,但我可以看到从 Promise 中解除 resolve 函数的问题,以便在事件进入时调用该函数。

class EventPropagation {
  constructor(id) {
    const button = document.getElementById(id);
    let _resolve;

    button.addEventListener("click", event => {
      if (_resolve) {
       _resolve(event);
      }
    });

    let _listen = () => {
      return new Promise(resolve => {
       _resolve = resolve;
      });
    }

    this.subscribe = async function*() {
      const result = await _listen();
      yield result;
      yield * this.subscribe();
    }
  }
}

async function example() {
  const eventPropagation = new EventPropagation("btn");
  for await (const event of eventPropagation.subscribe()) {
    console.log(event);
  }
}

// call the example function
example();

我的问题是:有没有更好的方法来构建这样的东西?有很多事情需要考虑,比如同时出现多个事件或清理侦听器和订阅。我的目标不是最终​​得到一个响应式库,但我确实想创建小的透明函数来异步产生事件。

fiddle

2017 年 12 月 14 日编辑(根据 Bergi 的评论编辑)

异步生成器

Babel 和后来的一些插件;异步生成器不是问题:

const throttle = ms => new Promise(resolve => setTimeout(resolve, ms));

const getData = async() => {
    const randomValue = Math.floor(Math.random() * 5000 + 1);
    await throttle(randomValue);
    return `The random value was: ${randomValue}`;
}

async function* asyncRandomMessage() {
    const message = await getData();
    yield message;

    // recursive call
    yield *asyncRandomMessage();
}

async function example() {
    for await (const message of asyncRandomMessage()) {
        console.log(message);
    }
}

// call it at your own risk, it does not stop
// example();

我想知道的是如何将一系列单独的回调调用转换为异步流。我无法想象这个问题没有得到解决。当我查看在 cmets 中显示的 Bergi 库时,我看到了与我所做的相同的实现,即:“将解析和拒绝函数存储在事件处理程序可以调用它们的地方。”我无法想象这将是解决这个问题的正确方法。

【问题讨论】:

  • 看起来您正在尝试实现异步生成器。这些还不存在,但是如果您查看提案,您还会发现this issue 甚至library doing all the work for you。另请注意,Observables 可能更适合表示 DOM 事件。
  • 感谢您的评论和链接。我想将此模式用于每种类型的异步流:DOM 事件、Firebase、WebWorkers 等。我正在为所有这些异步序列寻找一种易于实现的解决方案。如果我不能像这样构建它,我会定义。使用观察者模式。
  • 看看this answer

标签: javascript async-await


【解决方案1】:

你需要一个事件桶,这里是一个例子:

function evtBucket() {
  const stack = [],
                iterate = bucket();

  var next;

  async function * bucket() {
        while (true) {
            yield new Promise((res) => {
                if (stack.length > 0) {
                    return res(stack.shift());
                }

                next = res;
            });
        }
  }  

  iterate.push = (itm) => {
    if (next) {
      next(itm);
      next = false;
      return;
    }

    stack.push(itm);
  }  

  return iterate;
}

;(async function() {
  let evts = evtBucket();

  setInterval(()=>{
    evts.push(Date.now());
    evts.push(Date.now() + '++');
  }, 1000);

  for await (let evt of evts) {
    console.log(evt);
  }
})();

【讨论】:

    【解决方案2】:

    到目前为止,我最好的解决方案是拥有一个内部 EventTarget,它在将新事件添加到 queue 数组时调度事件。这就是我一直在为 JS 模块库(包括这里使用的模块)所做的工作。我不喜欢它……但它有效。

    注意:这也为多个地方的事件侦听器处理新的AbortSignal 选项。

    export function isAborted(signal) {
        if (signal instanceof AbortController) {
            return signal.signal.aborted;
        } else if (signal instanceof AbortSignal) {
            return signal.aborted;
        } else {
            return false;
        }
    }
    
    export async function when(target, event, { signal } = {}) {
      await new Promise(resolve => {
        target.addEventListener(event, resolve, { once: true, signal });
      });
    }
    
    export async function *yieldEvents(what, event, { capture, passive, signal } = {}) {
        const queue = [];
        const target = new EventTarget();
    
        what.addEventListener(event, event => {
            queue.push(event);
            target.dispatchEvent(new Event('enqueued'));
        }, { capture, passive, signal });
    
        while (! isAborted(signal)) {
            if (queue.length === 0) {
                await when(target, 'enqueued', { signal }).catch(e => {});
            }
    
            /**
             * May have aborted between beginning of loop and now
             */
            if (isAborted(signal)) {
                break;
            } else {
                yield queue.shift();
            }
        }
    }
    

    【讨论】:

    • 您能否添加一个示例来说明您将如何使用它?什么是信号?它从何而来?捕获和被动呢?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-18
    • 1970-01-01
    • 2011-01-30
    • 1970-01-01
    • 2016-03-17
    • 1970-01-01
    相关资源
    最近更新 更多