Since Promise.resolve().then(...) is used for deferring to another tick, any errors thrown inside render () { ... } gets silently thrown away. At least in Safari TP 15, I think that some other browsers reports unhandled rejections.
This is very unintuitive when developing since there is no way to see what went wrong.
This is the commit that introduced the current behavior: https://github.com/developit/preact/commit/6954d7549f35b5c32421c6855ee29ffda01137d1
Hmm - this sortof seems like a bug in Safari?
You can observe promise rejections globally:
addEventListener('unhandledrejection', e => {
console.log('Promise rejected: '+e.reason, e.promise);
});
Sadly Safari doesn't have a PromiseRejectionEvent so the suggested workaround doesn't work :(
Promise rejection is really hard since there often is no way to know whether or not the rejection was intentional. In this specific case though we know that the rejection was always unintentional, so I think it would be nice to report the error somehow...
How about only opting in for the Promise.resolve().then(...) optimization if there is support for reporting promise rejections?
The thing is, that reduces the quality of the entire app in Safari because the error handling is missing. It seem like this is the kind of thing that would be fixed as Safari 15 transitions out of Technical Preview status? Using setTimeout() to debounce instead of Microtasks (via promises) seems like a hefty price to pay.
I wonder if anyone has reported this to the Safari team?
This is definitely a Safari bug, they should log unhandled rejections.
Cool, I might close this and the PR's out for now, but I'd really love to get some more insight into why they seem to have opted for ignoring rejections in the console. Maybe they are planning on having that as a setting?
If it continues to be a bad user experience for devs, preact could do:
p.catch(err => {
console.error(err);
throw err;
});
鈥t the end of its promise chains.
That looks similar to @LinusU's solution but I believe it'd force the correct stack trace. Still, not sure it's worth the cost given that this is currently an unreleased version of Safari.
It affects released versions of Safari (since they supported promises). Totally understand the stack trace issue though. Not sure what the right answer is here.
I've just run into this confusion this morning and confirmed that this issue still applies to the stable version of Safari and the tech preview.
It looks like this is actively being worked on upstream.
Until this is resolved in Safari or worked around in Preact, you can ensure logging of errors during async render by overriding the function which queues up re-renders of dirty components:
import { options } from 'preact';
// Report errors during async component rendering in browsers that do not report
// uncaught promise rejections.
// See https://github.com/developit/preact/issues/357
options.debounceRendering = f =>
Promise.resolve()
.then(f)
.catch(err => console.error('Error during rendering: ', err));
Do you mind if I re-open this @developit since it is a pretty confusing experience out of the box in a major browser?
@robertknight Yes, absolutely we can re-open. FWIW I think we should probably switch (back) to setTimeout for debouncing since it fixes this and is smaller.
Closing since 8.0 now uses setTimeout() which propagates errors properly.
Most helpful comment
@robertknight Yes, absolutely we can re-open. FWIW I think we should probably switch (back) to
setTimeoutfor debouncing since it fixes this and is smaller.