Comments (1)
OMG. We have hundreds of Sentry issues that I've been debugging for weeks and weeks on and off, made so much worse by the really poor unhandled promise rejection stack I was getting in our react-native environment made this immensely difficult to track down. Turns out I was trusting the types too much and thought it was safe to do
myPromiseFn().finally()
Because the types say that the argument to finally is optional. I was doing this throughout my code because I was otherwise getting ESLint warnings saying I needed to await
the promise that was returned. Doing this was my way of communicating that I know what I'm doing and do not intend to await
this promise (in a useEffect()
for example).
tl;dr
This library requires a function to be provided to finally
and if not provided it throws with:
TypeError: undefined is not a function
Types Expectation
The types I find when clicking through to the definition of finally()
are as follows:
interface Promise<T> {
finally(onfinally?: (() => void) | undefined | null): Promise<T>
}
These types are provided directly from Typescript.
SEO for others in pain
Warning message I was receiving:
Possible Unhandled Promise Rejection (id: 0): TypeError: undefined is not a function
Sentry polyfills promise to make it nearly identical to the normal react-native promise, but also adds in sentry tracking of unhandled promise exceptions. This was the specific usage of this library I found.
Trying to find the original stack trace of unhandled promise rejections in this particular scenario was gruelingly difficult as the stack never included relevant code, just library code like the following:
(When Sentry was normally polyfilling)
TypeError: undefined is not a function
at anonymous (http://127.0.0.1:19000/index.bundle?platform=ios&dev=true&hot=false:125276:31)
at tryCallOne (http://127.0.0.1:19000/index.bundle?platform=ios&dev=true&hot=false:125101:16)
at anonymous (http://127.0.0.1:19000/index.bundle?platform=ios&dev=true&hot=false:125182:27)
at apply (native)
at anonymous (http://127.0.0.1:19000/index.bundle?platform=ios&dev=true&hot=false:26192:26)
at _callTimer (http://127.0.0.1:19000/index.bundle?platform=ios&dev=true&hot=false:26111:17)
at _callReactNativeMicrotasksPass (http://127.0.0.1:19000/index.bundle?platform=ios&dev=true&hot=false:26141:17)
at callReactNativeMicrotasks (http://127.0.0.1:19000/index.bundle?platform=ios&dev=true&hot=false:26304:44)
at __callReactNativeMicrotasks (http://127.0.0.1:19000/index.bundle?platform=ios&dev=true&hot=false:19761:46)
at anonymous (http://127.0.0.1:19000/index.bundle?platform=ios&dev=true&hot=false:19573:45)
at __guard (http://127.0.0.1:19000/index.bundle?platform=ios&dev=true&hot=false:19745:15)
at flushedQueue (http://127.0.0.1:19000/index.bundle?platform=ios&dev=true&hot=false:19572:21)
at callFunctionReturnFlushedQueue (http://127.0.0.1:19000/index.bundle?platform=ios&dev=true&hot=false:19557:33)
(When Sentry was set to enabled: false
)
WARN Possible Unhandled Promise Rejection (id: 5):
TypeError: undefined is not a function
TypeError: undefined is not a function
at anonymous (/Users/distiller/react-native/sdks/hermes/build_iphonesimulator/lib/InternalBytecode/InternalBytecode.js:339:28)
at tryCallOne (/Users/distiller/react-native/sdks/hermes/build_iphonesimulator/lib/InternalBytecode/InternalBytecode.js:53:16)
at anonymous (/Users/distiller/react-native/sdks/hermes/build_iphonesimulator/lib/InternalBytecode/InternalBytecode.js:139:27)
at apply (native)
at anonymous (http://127.0.0.1:19000/index.bundle?platform=ios&dev=true&hot=false:26192:26)
at _callTimer (http://127.0.0.1:19000/index.bundle?platform=ios&dev=true&hot=false:26111:17)
at _callReactNativeMicrotasksPass (http://127.0.0.1:19000/index.bundle?platform=ios&dev=true&hot=false:26141:17)
at callReactNativeMicrotasks (http://127.0.0.1:19000/index.bundle?platform=ios&dev=true&hot=false:26304:44)
at __callReactNativeMicrotasks (http://127.0.0.1:19000/index.bundle?platform=ios&dev=true&hot=false:19761:46)
at anonymous (http://127.0.0.1:19000/index.bundle?platform=ios&dev=true&hot=false:19573:45)
at __guard (http://127.0.0.1:19000/index.bundle?platform=ios&dev=true&hot=false:19745:15)
at flushedQueue (http://127.0.0.1:19000/index.bundle?platform=ios&dev=true&hot=false:19572:21)
at callFunctionReturnFlushedQueue (http://127.0.0.1:19000/index.bundle?platform=ios&dev=true&hot=false:19557:33)
Libs we are using:
"expo": "~47.0.14",
"sentry-expo": "~5.0.0",
"@sentry/react-native": "4.2.2",
"react-native": "0.70.8",
Identifying the offending line of code
I tried a few different StackOverflow answers for trying to find the real stack trace of the promise that was causing the issue I was seeing. As noted the stack trace that is printed does not end up leading to any code I have written, which made it questionable whether it was my fault or potentially some other library causing the issue.
After trying to use bluebird as suggested here I curiously found that the warning went away. Now I know it was because Bluebird promise implementation does not require a finally parameter!
In the end, I did the following to identify the offending line of code (sorry this isn't a patch-package patch as we do not use that lib See patch package below)
Manually add three lines to these two files
File 1: node_modules/promise/setimmediate/core.js
Add the following line of code in the function Promise(fn) {...
function (line 61 for me).
this._stack = (new Error()).stack;
File 2: node_modules/promise/setimmediate/rejection-tracking.js
Inside the enable
function where a setTimeout
is called (line 47 for me) with:
onUnhandled.bind(null, promise._51)
Replace it with:
onUnhandled.bind(null, promise._51, promise)
Then in the onUnhandled
function just add a 2nd parameter "promise" and console log there and you'll see the _stack
field printed out with the original location where the Promise was created.
function onUnhandled(id, promise) { // line 60 for me
console.log("rejection-tracking:61 - onUnhandled", id, promise)
Or use this Patch with `patch-package`
diff --git a/node_modules/promise/setimmediate/core.js b/node_modules/promise/setimmediate/core.js
index a84fb3d..1389b85 100644
--- a/node_modules/promise/setimmediate/core.js
+++ b/node_modules/promise/setimmediate/core.js
@@ -58,6 +58,14 @@ function Promise(fn) {
if (typeof fn !== 'function') {
throw new TypeError('Promise constructor\'s argument is not a function');
}
+ if(!__DEV__) {
+ console.warn('WARNING - Promise Stack Trace Debugging is NOT recommended for production code.');
+ }
+ const stackTraceLimit = Error.stackTraceLimit
+ Error.stackTraceLimit = Infinity
+ this._stack = (new Error()).stack;
+ Error.stackTraceLimit = stackTraceLimit
+
this._40 = 0;
this._65 = 0;
this._55 = null;
diff --git a/node_modules/promise/setimmediate/rejection-tracking.js b/node_modules/promise/setimmediate/rejection-tracking.js
index 10ccce3..75f2fd1 100644
--- a/node_modules/promise/setimmediate/rejection-tracking.js
+++ b/node_modules/promise/setimmediate/rejection-tracking.js
@@ -44,7 +44,7 @@ function enable(options) {
displayId: null,
error: err,
timeout: setTimeout(
- onUnhandled.bind(null, promise._51),
+ onUnhandled.bind(null, promise._51, promise),
// For reference errors and type errors, this almost always
// means the programmer made a mistake, so log them after just
// 100ms
@@ -57,7 +57,8 @@ function enable(options) {
};
}
};
- function onUnhandled(id) {
+ function onUnhandled(id, promise) {
+ console.warn('Unhandled Promise Rejection', id, promise)
if (
options.allRejections ||
matchWhitelist(
Although I got a bundled stack trace I was still able to track it down when the code was not minified.
NOTE: This is NOT performant and SHOULD NOT be included in any production builds as calling (new Error()).stack
on every promise constructor will slow things down a lot. For debugging this is not an issue though.
I'd love for someone to tell me how I could have done this an alternative way as I feel it is ridiculous I had to go through these steps.
Edit: I decided to create a patch-package patch to make it easier to do this again in the future:
from promise.
Related Issues (20)
- Promise do not change to "resolve" mode when calling the function again. HOT 4
- can Promise.resolve replace by code like this? HOT 2
- Add an additional method HOT 3
- promise pending HOT 1
- Discuss ways to avoid `new Function` in denodeify HOT 1
- Why promise. then is faster than setTimeout HOT 1
- who can tell me that what is the meaning of the code? HOT 2
- promise.race() missing from the API description in README HOT 4
- Promise.race resolves the combined Promise multiple times, which emits an error/warning event in node HOT 1
- Questions regarding unhandled rejections HOT 4
- Difference with native Promise HOT 2
- if (fn === noop) return; Can't judge correctly HOT 1
- Catch works earlier if I pass reject as second callback HOT 1
- code refactor. use new.target to detect if promise is called with new instead of this check HOT 1
- How can I build a setimmediate flavor that is not uglified? HOT 1
- optifine bugs HOT 1
- It's difference between native and shim HOT 1
- Promise.allSettled ? HOT 2
- [BUG]Why this promise execute queue is not same with v8 Promise。 HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from promise.