【发布时间】:2015-03-24 15:20:15
【问题描述】:
考虑以下用 ES6 编写的代码:
function waitForMessage() {
return new Promise((resolve, reject) => {
function handler(event) {
resolve(event);
window.removeEventListener('message', handler);
};
window.addEventListener('message', handler);
});
}
function loop(event) {
// do something (synchronous) with event
waitForMessage().then(loop);
}
waitForMessage().then(loop);
在这段代码中,waitForMessage 安装了一个事件处理程序,等待消息到达当前窗口。一旦它到达,waitForMessage 返回的 Promise 将被解析并移除事件处理程序。
在loop 中,只要通过解决前一个承诺而排队的作业正在运行,waitForMessage 就会生成一个新的承诺。
现在我的问题是 loop 是否可能由于时间问题而无法在窗口中发布所有消息:如果 Promise.prototype.resolve 排队的作业并不总是在浏览器事件循环中排队的任何任务之前运行,它可能假设message 事件在window 开始调度,而当前没有处理程序正在侦听此事件。
标准对这些不同类型的作业/任务的时间安排有何规定,即解决 Promise 的回调和来自 ES6 世界之外的事件分派?
(我只是以message事件为例,我对其他事件同样感兴趣,比如click或popstate事件。)
P.S.:由于在下面的 cmets 中已经多次询问过这个问题,让我用上面的代码描述一下我所希望的:
我想使用 ES6 特性来避免在我的代码中过多地处理回调,并确保及时删除添加的事件侦听器以避免内存泄漏。因此,我按照这些思路写了一些东西:
const looper = (element, type, generator) => (... args) => new Promise((resolve, reject) => {
const iterator = generator(...args);
const listener = (event) => {
try {
let {done, value} = iterator.next(event);
} catch (error) {
reject(error);
element.removeEventListener(type, listener);
}
if (done) {
resolve(value);
element.removeEventListener(type, listener);
}
}
element.addEventListener(type, listener);
listener();
});
const loop = (element, type, generator) => looper(element, type, generator)();
使用此代码,我可以执行以下操作:
loop(window, 'message', function *() {
event = yield;
// do something synchronous with event
if (shouldStopHere) {
return result;
}
});
此代码不受我的问题所涉及的问题的影响;只创建一个promise,并且事件处理程序只附加和删除一次。当内部函数返回时,事件处理程序的删除是有保证的。
众所周知,ES6 中的生成器也可以用于处理 Promise(就像 Python 3.4 中的 asyncio 包一样)。有人提议 ES7 为这些异步函数包含一些糖,即https://github.com/lukehoban/ecmascript-asyncawait。我希望使用这种糖(目前由 Traceur 支持)来糖化我上面的 loop 函数。然而,提议的异步函数只处理承诺,所以我尝试以一种产生承诺结果的方式重写我的循环代码,我在问题的开头发布了这个结果。
【问题讨论】:
-
你对这个承诺有点自欺欺人。 Promise 不应该真正用于可能发生多次的事件。
-
我看不出为什么在这种情况下不应使用承诺。无论如何,问题仍然存在。
-
好吧,因为 Promise 是一个对象,它代表了一些未来可用的数据,比如异步函数调用的结果。它不适合可能会或可能不会到达的东西,或者会不止一次到达的东西。
-
@Marc 与将其放入事件处理程序而不涉及承诺相比有什么优势?
-
无论这种方法有什么其他问题,我认为你需要说
function loop(event) { return waitForMessage().then(loop); }。注意return。
标签: javascript events promise ecmascript-6