好问题。让我从一件事开始:Promise 不是事件发射器。
让我重申一下,因为这是一个经常出现的误解。 Promise 是不是事件发射器。随着进程的进展,它们可能会被黑客入侵成一种残缺的事件发射器,但最终还是会如此。 Promise 不是事件发射器。
承诺
它们是什么? Promise 是一个值的“盒子”,您可以在某个时候使用.then 方法打开它,然后将结果放入另一个盒子中。不多也不少。
就像你说的,承诺是一次性的。如果您的活动是一次性活动 - 那么承诺绝对可以。从本质上讲,您的事件是一种不测事件,并且承诺在大多数情况下会更好地对其进行建模。
作为发射器的承诺
使用 Promise 作为事件发射器的问题在于组合,Promise 中的进程事件根本没有很好地组合。 Promise 链和组合,而事件则没有。这就是为什么 Q 库在 v2 中放弃进度以支持估计的原因。这就是 ECMAScript 6 中从未包含进程的原因。
事件发射器本身就是一个完美的抽象,当它们是建模关系的正确工具时使用事件发射器(pub-sub),当它们是建模关系的正确工具时使用承诺,使用流当它们是建模您的关系的正确工具时。只是不要对所有事情都使用一种工具,因为这(根据经验)只会给你带来很多痛苦。
我在问题中描述的内容真的很酷,那又如何?
您在寻找什么?哦,那是存在的。虽然它也有自己的一系列问题,但实际上它非常棒。
您正在寻找的东西称为 FRP - 函数式反应式编程。有很多图书馆可以做到这一点,其中最好的(在我看来)是 BaconJS。
FRP 有你所说的 observables 的概念。以下是来自BaconJS 网站的计数器示例:
var up = $('#up').asEventStream('click');
var down = $('#down').asEventStream('click');
var counter =
// map up to 1, down to -1
up.map(1).merge(down.map(-1))
// accumulate sum
.scan(0, function(x,y) { return x + y });
// assign observable value to jQuery property text
counter.assign($('#counter'), 'text');
与链式中的 promise 非常相似,但不代表直接延续,而是连续流式传输和接收器。
FRP 是 Haskell 等函数式语言中非常常见和开发的范例,在 JavaScript 中非常适用。这还不是很常见还,它有自己的缺点,但它肯定像你的想法一样思考。
所以,简短回顾一下:
- Promise 不是事件发射器。 Promise 非常棒,可以解决很多有趣的并发问题 - 但它们并不是所有解耦流控制的灵丹妙药。
- FRP 是您想出但尚未制定的这个很酷的东西。它具有与您在问题中描述的完全相同的可观察对象和事件的概念。
此外,您可以在自己不知道范式的情况下思考范式,为自己拍拍背。老实说,这是你应得的。