node/lib/internal/process/promises.js
Joyee Cheung 457603e961
src: move process.nextTick and promise setup into node_task_queue.cc
This patch:

- Moves the process.nextTick and promise setup C++ code into
  node_task_queue.cc which is exposed as
  `internalBinding('task_queue')`
- Makes `lib/internal/process/promises.js` and
  `lib/internal/process/next_tick.js` as side-effect-free
  as possible
- Removes the bootstrapper object being passed into
  `bootstrap/node.js`, let `next_tick.js` and `promises.js`
  load whatever they need from `internalBinding('task_queue')`
  instead.
- Rename `process._tickCallback` to `runNextTicks` internally
  for clarity but still expose it as `process._tickCallback`.

PR-URL: https://github.com/nodejs/node/pull/25163
Refs: https://github.com/nodejs/node/issues/24961
Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
Reviewed-By: Anatoli Papirovski <apapirovski@mac.com>
2018-12-24 07:57:15 +08:00

127 lines
3.9 KiB
JavaScript

'use strict';
const { safeToString } = internalBinding('util');
const {
promiseRejectEvents
} = internalBinding('task_queue');
const maybeUnhandledPromises = new WeakMap();
const pendingUnhandledRejections = [];
const asyncHandledRejections = [];
let lastPromiseId = 0;
function promiseRejectHandler(type, promise, reason) {
switch (type) {
case promiseRejectEvents.kPromiseRejectWithNoHandler:
return unhandledRejection(promise, reason);
case promiseRejectEvents.kPromiseHandlerAddedAfterReject:
return handledRejection(promise);
case promiseRejectEvents.kPromiseResolveAfterResolved:
return resolveError('resolve', promise, reason);
case promiseRejectEvents.kPromiseRejectAfterResolved:
return resolveError('reject', promise, reason);
}
}
function resolveError(type, promise, reason) {
// We have to wrap this in a next tick. Otherwise the error could be caught by
// the executed promise.
process.nextTick(() => {
process.emit('multipleResolves', type, promise, reason);
});
}
function unhandledRejection(promise, reason) {
maybeUnhandledPromises.set(promise, {
reason,
uid: ++lastPromiseId,
warned: false
});
pendingUnhandledRejections.push(promise);
return true;
}
function handledRejection(promise) {
const promiseInfo = maybeUnhandledPromises.get(promise);
if (promiseInfo !== undefined) {
maybeUnhandledPromises.delete(promise);
if (promiseInfo.warned) {
const { uid } = promiseInfo;
// Generate the warning object early to get a good stack trace.
// eslint-disable-next-line no-restricted-syntax
const warning = new Error('Promise rejection was handled ' +
`asynchronously (rejection id: ${uid})`);
warning.name = 'PromiseRejectionHandledWarning';
warning.id = uid;
asyncHandledRejections.push({ promise, warning });
return true;
}
}
return false;
}
const unhandledRejectionErrName = 'UnhandledPromiseRejectionWarning';
function emitWarning(uid, reason) {
// eslint-disable-next-line no-restricted-syntax
const warning = new Error(
'Unhandled promise rejection. This error originated either by ' +
'throwing inside of an async function without a catch block, ' +
'or by rejecting a promise which was not handled with .catch(). ' +
`(rejection id: ${uid})`
);
warning.name = unhandledRejectionErrName;
try {
if (reason instanceof Error) {
warning.stack = reason.stack;
process.emitWarning(reason.stack, unhandledRejectionErrName);
} else {
process.emitWarning(safeToString(reason), unhandledRejectionErrName);
}
} catch {}
process.emitWarning(warning);
emitDeprecationWarning();
}
let deprecationWarned = false;
function emitDeprecationWarning() {
if (!deprecationWarned) {
deprecationWarned = true;
process.emitWarning(
'Unhandled promise rejections are deprecated. In the future, ' +
'promise rejections that are not handled will terminate the ' +
'Node.js process with a non-zero exit code.',
'DeprecationWarning', 'DEP0018');
}
}
function emitPromiseRejectionWarnings() {
while (asyncHandledRejections.length > 0) {
const { promise, warning } = asyncHandledRejections.shift();
if (!process.emit('rejectionHandled', promise)) {
process.emitWarning(warning);
}
}
let maybeScheduledTicks = false;
let len = pendingUnhandledRejections.length;
while (len--) {
const promise = pendingUnhandledRejections.shift();
const promiseInfo = maybeUnhandledPromises.get(promise);
if (promiseInfo !== undefined) {
promiseInfo.warned = true;
const { reason, uid } = promiseInfo;
if (!process.emit('unhandledRejection', reason, promise)) {
emitWarning(uid, reason);
}
maybeScheduledTicks = true;
}
}
return maybeScheduledTicks || pendingUnhandledRejections.length !== 0;
}
module.exports = {
promiseRejectHandler,
emitPromiseRejectionWarnings
};