We also have at least two more differences between Squeak promises and ECMAScript promises at the moment:
Promises/A+ demands that the onFulfilled and onRejected callbacks are called with a "clean" stack. [1] First, the Squeak implementation does not do that. It evaluates the callback blocks immediately when the promise changes state, and thus in the exception environment of whatever caused that. Second, this implies that a Promises/A+-conforming application cannot rely on try..catch/on:do: exception handlers anyway (not around the send of #then:, but also not around the send of #resolveWith:). Thus, it would be somewhat futile to expect exceptions to be signalled out of the promise callback blocks. The repeated stack unwinding, or ubiquitous asynchrony, also means that promises as in Promises/A+ are fundamentally not compatible with the "error -> edit any method on stack -> restart" debugging style that we are used to in Smalltalk. At least not without a debugger that is specialized in promises.
ECMAScript specifies that if no onRejected callback is provided to .then(), or rather if the onRejected argument is not callable, then rejecting the promise will throw the reason for the rejection. In Squeak terms: `p then: [:v | ...]. "===" p then: [:v | ...] ifRejected: [:e | e signal]`. In ECMAScript 6 this is specified almost straightforward [2], whereas in the current standard it seems to be more complicated, but with the same end result of throwing the error. In practice, an uncaught exception in an ECMAScript promise chain may end up in the console. The Squeak equivalent would be to show a debugger (with an unrevealing stack trace if we would take the asynchrony requirement seriously) or to pop up a Transcript message. But in Squeak there is no default rejected handler that signals the error, so currently just nothing happens. Well, the promise gets rejected, but there is no visible reaction to an unhandled error.
Taking that into account, we could remove the ifRejected: block from the previous example and make further expectations:
promise := (1 future / 0) then: [:result | result + 3]. "<= inspect it
=> Actual: rejected with ZeroDivide.
Preferable: show debugger on ZeroDivide.
Expected on abandon: announce that the promise was rejected with an uncaught error.
Expected on proceed: new debugger "ZeroDivide does not understand +".
Expected on abandon: announce as above, but referring to the MNU"
The "announcement" could be yet another debugger, the Transcript, or something customizable. The debugger might not always be the right choice, let's have another example:
p := [Promise new rejectWith: 0] future value then: [:result | result + 3].
"...send some further messages..."
p whenRejected: [].
The current ECMAScript standard made me aware of this race condition [3]. If the example above is not run in the UI process, p might be rejected before the empty handler gets attached or afterwards. If it is rejected afterwards, a debugger must not appear. If it is rejected before, we might show a debugger, only to regret it a few microseconds later.