1
1
'use strict' ;
2
2
3
- const { safeToString } = internalBinding ( 'util' ) ;
3
+ const { Object } = primordials ;
4
+
5
+ const {
6
+ safeToString
7
+ } = internalBinding ( 'util' ) ;
4
8
const {
5
9
tickInfo,
6
10
promiseRejectEvents : {
9
13
kPromiseResolveAfterResolved,
10
14
kPromiseRejectAfterResolved
11
15
} ,
12
- setPromiseRejectCallback
16
+ setPromiseRejectCallback,
17
+ triggerFatalException
13
18
} = internalBinding ( 'task_queue' ) ;
14
19
15
20
// *Must* match Environment::TickInfo::Fields in src/env.h.
@@ -20,6 +25,15 @@ const pendingUnhandledRejections = [];
20
25
const asyncHandledRejections = [ ] ;
21
26
let lastPromiseId = 0 ;
22
27
28
+ const states = {
29
+ none : 0 ,
30
+ warn : 1 ,
31
+ strict : 2 ,
32
+ default : 3
33
+ } ;
34
+
35
+ let state ;
36
+
23
37
function setHasRejectionToWarn ( value ) {
24
38
tickInfo [ kHasRejectionToWarn ] = value ? 1 : 0 ;
25
39
}
@@ -29,6 +43,10 @@ function hasRejectionToWarn() {
29
43
}
30
44
31
45
function promiseRejectHandler ( type , promise , reason ) {
46
+ if ( state === undefined ) {
47
+ const { getOptionValue } = require ( 'internal/options' ) ;
48
+ state = states [ getOptionValue ( '--unhandled-rejections' ) || 'default' ] ;
49
+ }
32
50
switch ( type ) {
33
51
case kPromiseRejectWithNoHandler :
34
52
unhandledRejection ( promise , reason ) ;
@@ -59,6 +77,7 @@ function unhandledRejection(promise, reason) {
59
77
uid : ++ lastPromiseId ,
60
78
warned : false
61
79
} ) ;
80
+ // This causes the promise to be referenced at least for one tick.
62
81
pendingUnhandledRejections . push ( promise ) ;
63
82
setHasRejectionToWarn ( true ) ;
64
83
}
@@ -85,14 +104,16 @@ function handledRejection(promise) {
85
104
86
105
const unhandledRejectionErrName = 'UnhandledPromiseRejectionWarning' ;
87
106
function emitWarning ( uid , reason ) {
88
- // eslint-disable-next-line no-restricted-syntax
89
- const warning = new Error (
107
+ if ( state === states . none ) {
108
+ return ;
109
+ }
110
+ const warning = getError (
111
+ unhandledRejectionErrName ,
90
112
'Unhandled promise rejection. This error originated either by ' +
91
- 'throwing inside of an async function without a catch block, ' +
92
- 'or by rejecting a promise which was not handled with .catch(). ' +
93
- `(rejection id: ${ uid } )`
113
+ 'throwing inside of an async function without a catch block, ' +
114
+ 'or by rejecting a promise which was not handled with .catch(). ' +
115
+ `(rejection id: ${ uid } )`
94
116
) ;
95
- warning . name = unhandledRejectionErrName ;
96
117
try {
97
118
if ( reason instanceof Error ) {
98
119
warning . stack = reason . stack ;
@@ -108,7 +129,7 @@ function emitWarning(uid, reason) {
108
129
109
130
let deprecationWarned = false ;
110
131
function emitDeprecationWarning ( ) {
111
- if ( ! deprecationWarned ) {
132
+ if ( state === states . default && ! deprecationWarned ) {
112
133
deprecationWarned = true ;
113
134
process . emitWarning (
114
135
'Unhandled promise rejections are deprecated. In the future, ' +
@@ -133,18 +154,57 @@ function processPromiseRejections() {
133
154
while ( len -- ) {
134
155
const promise = pendingUnhandledRejections . shift ( ) ;
135
156
const promiseInfo = maybeUnhandledPromises . get ( promise ) ;
136
- if ( promiseInfo ! == undefined ) {
137
- promiseInfo . warned = true ;
138
- const { reason , uid } = promiseInfo ;
139
- if ( ! process . emit ( 'unhandledRejection' , reason , promise ) ) {
140
- emitWarning ( uid , reason ) ;
141
- }
142
- maybeScheduledTicks = true ;
157
+ if ( promiseInfo = == undefined ) {
158
+ continue ;
159
+ }
160
+ promiseInfo . warned = true ;
161
+ const { reason , uid } = promiseInfo ;
162
+ if ( state === states . strict ) {
163
+ fatalException ( reason ) ;
143
164
}
165
+ if ( ! process . emit ( 'unhandledRejection' , reason , promise ) ||
166
+ // Always warn in case the user requested it.
167
+ state === states . warn ) {
168
+ emitWarning ( uid , reason ) ;
169
+ }
170
+ maybeScheduledTicks = true ;
144
171
}
145
172
return maybeScheduledTicks || pendingUnhandledRejections . length !== 0 ;
146
173
}
147
174
175
+ function getError ( name , message ) {
176
+ // Reset the stack to prevent any overhead.
177
+ const tmp = Error . stackTraceLimit ;
178
+ Error . stackTraceLimit = 0 ;
179
+ // eslint-disable-next-line no-restricted-syntax
180
+ const err = new Error ( message ) ;
181
+ Error . stackTraceLimit = tmp ;
182
+ Object . defineProperty ( err , 'name' , {
183
+ value : name ,
184
+ enumerable : false ,
185
+ writable : true ,
186
+ configurable : true ,
187
+ } ) ;
188
+ return err ;
189
+ }
190
+
191
+ function fatalException ( reason ) {
192
+ let err ;
193
+ if ( reason instanceof Error ) {
194
+ err = reason ;
195
+ } else {
196
+ err = getError (
197
+ 'UnhandledPromiseRejection' ,
198
+ 'This error originated either by ' +
199
+ 'throwing inside of an async function without a catch block, ' +
200
+ 'or by rejecting a promise which was not handled with .catch().' +
201
+ ` The promise rejected with the reason "${ safeToString ( reason ) } ".`
202
+ ) ;
203
+ err . code = 'ERR_UNHANDLED_REJECTION' ;
204
+ }
205
+ triggerFatalException ( err , true /* fromPromise */ ) ;
206
+ }
207
+
148
208
function listenForRejections ( ) {
149
209
setPromiseRejectCallback ( promiseRejectHandler ) ;
150
210
}
0 commit comments