Closed abel-matsec closed 1 year ago
All good then, I suppose 😸?
Oh weird. Yeah, not all good :P didn't realize that mentioning this PR in our internal repo would close the issue... re-opening
These are async functions, when you await
anything in them even if that promise is already fulfilled or it's not even a promise it will defer a microtick before continuing to run.
The reason your bluebird example sometimes "works" is that bluebird internally knows some functions are supposed to be async like setTimeout so it skips that microtick which is done here: https://github.com/petkaantonov/bluebird/blob/master/src/timers.js#L32 and subsequently the microtick is skipped here: https://github.com/petkaantonov/bluebird/blob/master/src/promise.js#L714-L717
Basically bluebird is making the assumption setTimeout is async which is fair and the mocks break that assumption which messes with timing. A little like if you do Promise.setScheduler(fn => fn())
.
This is not a bug in fake-timers and there is nothing we can do to make the native promises behave like bluebird anyway - you need to await
those function calls :)
Thanks for the explanation! It might be beneficial to add some notes to the documentation for the sync methods to warn folks of edge cases like this they might run into. It was very surprising and non-obvious for us, and a helpful doc caveat could have saved a lot of time.
Thanks for the great work on this lib!
Thanks for the explanation! It might be beneficial to add some notes to the documentation for the sync methods to warn folks of edge cases like this they might run into. It was very surprising and non-obvious for us, and a helpful doc caveat could have saved a lot of time.
This is more of a bluebird caveat than a fake-timers one, though I agree a note about this potential behavior may be useful. Wanna open a docs PR :)?
@benjamingr I'm not sure it's just a bluebird caveat. I show in my above example me defining my own util (i.e. not using bluebird), and observing similar odd behavior. I can look into maybe starting a docs PR, though I'm not sure I'll be able to explain the subtleties of tick execution as well as you guys can, but I can take a crack at it regardless.
function delay<T>(ms: number, value?: T): Promise<T | undefined> {
return new Promise(($resolve, $reject) => {
setTimeout(() => {
$resolve(value);
}, ms);
});
}
What did you expect to happen?
In the below unit test, I would expect all of my
delayXResolved
values to betrue
. Alternatively, when usingtickAsync
all things behave as I would have expected.What actually happens
Only some of them are set to true. Subsequent calls show varying behavior of when the timers actually fire.
How to reproduce
Unit test demonstrating the issue with both sinonjs and fake-timers libs. Run via mocha with: