如何用 JavaScript 构建事件生成器

Mr.*_*din 3 javascript async-await

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

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

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();
Run Code Online (Sandbox Code Playgroud)

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

小提琴

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();
Run Code Online (Sandbox Code Playgroud)

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

小智 9

您需要一个事件存储桶,这是一个示例:

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);
  }
})();
Run Code Online (Sandbox Code Playgroud)