Stream vs Promise Asynchronicity

I recently hit a stumbling block whilst testing asynchronous javascript. My code needed to make a call to the server from the browser, so I decided to use a Promise as an abstraction in order to stub it out. I chose the bluebird library, as it’s incredibly popular, performant and adheres to the Promises/A+ spec.

Unfortunately when I stubbed out the server call with an immediately-resolved promise in my test, it always failed:

describe('bluebird', () => {
const getPromise = () =>
Promise.resolve(true);
it('should allow synchronous behaviour', () => {
let set = false;
getPromise()
.then(value => {
set = value;
});
expect(set).toBe(true);
});
});

It wasn’t obvious what was going wrong and by the time I started to pull my hair out I decided to change tack. I replaced the use of bluebird Promises with Rx Observables (the other common way of combining asynchronous operations), to create a stream with one value, which would be the server’s response. Lo and behold the code worked! I’ve created a fiddle to compare the (seemingly identical) tests:

https://jsfiddle.net/nickkell/91L05sta/

The reason is that libraries adhering to the Promises/A+ spec must be asynchronous, even if your code looks like it’s executing synchronously and immediately. It seems that observables don’t have this same requirement and can actually execute synchronously. I’ve created a fiddle that shows the previously failing test passing, by invoking the callback that jasmine gives you especially for this purpose:

https://jsfiddle.net/nickkell/7vo4nuxv/1/

I’m not sure which behaviour is more correct, but the synchronous execution of the Rx Observable was definitely more in-line with my mental model of the problem and I’ll be sticking with them from now on.

Leave a comment