jdonaldson / promhx

A promise and functional reactive programming library for Haxe
MIT License
145 stars 24 forks source link

Add ability to disable the event loop #58

Closed NoxHarmonium closed 9 years ago

NoxHarmonium commented 9 years ago

I like the event loop but sometimes the ability to disable it comes in handy. For example, to keep my unit tests simple it would be great to have the promises resolve synchronously.

I have simply added a compiler flag to execute any function that is enqueued in the event loop immediately rather than queuing it. The problem is, however, that it seems to cause all sorts of issues. The promhx unit tests are failing and when testing on my game project, errors and callbacks were not propagating properly at seemingly random places in the code.

I was wondering if you might be know what is causing this off the top of your head and make a suggestion to fix it in this pull request. Otherwise I will dig around the code more tomorrow and see if I can get to the bottom of it.

Thanks!

jdonaldson commented 9 years ago

I understand that you're trying to make your unit tests simple, but I think if you want to test the "real world behavior" of promises, you're going to need to take control of the event loop, and not discard it completely. Otherwise, you're missing a major "contract" for promises (deferred execution), and none of the unit tests that count on this feature will work properly.

If I wanted to disable the normal asynchronous behavior, I would override the EventLoop method with a dummy:

EventLoop.nextLoop = function(f){};

This will keep promhx from enqueueing the function with setTimeout in js. However, the promises will never resolve.

EventLoop keeps its own queue of pending resolves. You can tell it to try and finish this queue with the command EventLoop.finish(), which will continue to resolve asynchronous values in its queue until it is empty. Keep in mind that finishing the queue is straightforward for trivial tests, but the behavior gets more complicated when you use chaining, etc. If you're using promhx for anything useful at all, I would have to imagine you're using chaining, and you should probably be testing the chaining behavior as well.

If you're curious about the promhx unit tests, you can check out src/test/TestAllQueue.hx, which does the same basic approach I've outlined above. It hooks the EventLoop.finish() method into a test precheck callback, which means I don't even need to call it in the test itself.

hexonaut commented 9 years ago

One thing you should be concerned about while disabling the event loop altogether (which I have also tried and do not recommend), if you ever throw an error and no catchError, errorThen, errorPipe is present downstream then the error will be thrown. If you have any promises that resolve synchronously then it's very likely that an error handler has not been registered before any error you want to propagate has been thrown. For example:

Promise.error("Some Error").then(function (_) {
    //Do something
}).catchError(function (err) {
    //This will not be called with no event loop
});
NoxHarmonium commented 9 years ago

Thanks for your quick replies!

I don't know why I didn't think of looking at the promhx tests. EventLoop.finish(); is exactly what I needed so I could flush the event queue and make sure that all the state had settled down before doing my asserts in the basic tests.

I was about to keep arguing that there should be a way to skip the event queue but I kept reading about promises and issues like this I realised what you meant about the deferred execution contract of promises.

If I understand correctly, every time you chain on another .then() statement, it will be executed in the next loop. I am using promhx to implement reactive programming so I have lots of small transformation functions chained together triggered by events. If I have a chain of 10 small transformation functions and the game is running at 30 fps than I have a delay of 0.3s second from the time the original stream is resolved and the end of the chain is triggered. That is where the delay starts to become noticeable. Although it is subtle, it is noticeable in a game.

Can you think of any way to improve this apart from coalescing the individual functions into a single one?

jdonaldson commented 9 years ago

FWIW, there's been some changes in how setTimeout works recently in modern browsers. I need to update promhx accordingly. I'm trying to integrate this with some new haxe 3.2 features. I'm shooting for having this ready in a week or two.