So far, I've got a working solution, below, which give context to these questions but I'm not sure I fully understand setImmediate or if there is a better approach to try in this case.
I'm brand new to JS but an experienced programmer with several other languages including Python, C/C++ and Java.
My questions are these:
1. Is setImmediate guaranteed to always run after all pending promises have resolved, or just those that can be resolved (i.e. the resolutions are ready) in the next tick?
In my specific example, if the save process (which is asynchronous and started by the onPress handler) hung for, say, 5s (so the promise was there but did not resolve for that time) would the immediate wait until that pending promise had resolved or does setImmediate only guarantee that asyncronous resolutions that are ready (i.e. if save took a few ms and the resolution was ready, just queued for the event loop to deal with) are resolved before the callback runs?
I've read several online articles about how the JS event loop and setImmediate are supposed to work but I just seem to be getting more confused about this nuance of how it works. For example, one says:
[getImmediate callbacks are] executed in the next iteration of the event loop
From my current (lack-of) understanding that would mean it would run before promises had resolved, unless pending promises block the event loop from iterating but that would surely mean the event loop cannot process any other (new) events while there are asynchronous tasks (promises) still waiting for resolution, which this next description says is not the case:
a promise doesn't block anything and certainly does not block the event loop.
This either means setImmediate does not run after all pending promises, if it always runs in the next iteration of the event loop, or the above explanation "executed in the next iteration of the event loop" is incorrect.
A third description of the event loop describes it as a 7 phase process and that setImmediate is a distinct phase (number 6) in the loop that:
will process them until the immediate queue is exhausted
Again suggesting setImmediate will run before pending promises are resolved if they take a (computational) while.
Why am I worrying about this? Well, if setImmediate does run in the next iteration of the (or conclusion of the current) event loop and promises don't block the event loop (which would make most sense to me) I've got a race condition that it just happens my test code seems to be winning at the moment - if any of the asynchronous tasks took too long to resolve then the state/save would not have been done when the setImmediate callback ran to test the outcome triggered by the simulated 'press' event.
I hope I've made enough sense someone can figure out what I'm asking! I've tried to answer this myself but I've just become more confused by the differing descriptions of how the event loop and setImmediate actually work. I'd very much appreciate being pointed to some authoritative description of the internals of setImmediate and the event loop as Google doesn't seem to be being terribly helpful with this quest of mine.
2. This solution seems to work, and based on answers to this sort of problem I've found on this and other sites seems to be acceptable, but is there a better way? And if the answer to my first question is that there is a race condition here (due to how setImmediate and the event loop actually work) how do I eliminate it (i.e. wait for all pending promises to resolve before trying to test the outcome)?
My current solution looks like this:
for (let button in button_lang_map) {
it('button ' + button + ' sets corresponding language internally', done => {
let component = renderer(<SettingsChooseLanguage />);
component.query(button).simulate('press', {});
/*
* Make sure this runs after asynchronous button press processes.
* See https://github.com/airbnb/enzyme/issues/823 for example.
*/
setImmediate(() => {
expect(component.state().currentLanguage).toBe(button_lang_map[button]);
done();
});
});
it('language is saved when ' + button + ' button is pressed', done => {
let component = renderer(<SettingsChooseLanguage />);
component.query(button).simulate('press', {});
setImmediate(() => {
getLanguage().then(result => expect(result).toBe(button_lang_map[button]));
done();
});
});
}
The simulate method (from react-native-test-utils) does not return a promise (it's a void), even though the onPress handler is an async function. The above works, using setImmediate, to complete the test once all the promises have resolved (meaning the state has changed and the save has completed respectively) and is based on one (of several) solutions I found online.
Aucun commentaire:
Enregistrer un commentaire