Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions doc/api/util.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -1691,6 +1691,17 @@ console.log(util.isDeepStrictEqual(foo, bar, true));
See [`assert.deepStrictEqual()`][] for more information about deep strict
equality.

## `util.markPromiseAsHandled(promise)`

<!-- YAML
added: REPLACEME
-->

*`promise` {promise} The promise to mark as handled

Marks a promise as handled so that unhandled rejections are ignored and are not
reported to the `'unhandledrejection'` event.

## Class: `util.MIMEType`

<!-- YAML
Expand Down
8 changes: 8 additions & 0 deletions lib/util.js
Original file line numberDiff line numberDiff line change
Expand Up@@ -588,6 +588,13 @@ function deprecate(fn, msg, code, { modifyPrototype } = kEmptyObject) {
returninternalDeprecate(fn,msg,code,undefined,modifyPrototype);
}

functionmarkPromiseAsHandled(promise){
if(!types.isPromise(promise)){
thrownewERR_INVALID_ARG_TYPE('promise','Promise',promise);
}
binding.markPromiseAsHandled(promise);
}

// Keep the `exports =` so that various functions can still be monkeypatched
module.exports={
_errnoException,
Expand DownExpand Up@@ -618,6 +625,7 @@ module.exports = {
}
returninternalDeepEqual(a,b,skipPrototype);
},
markPromiseAsHandled,
promisify,
stripVTControlCharacters,
toUSVString(input){
Expand Down
23 changes: 23 additions & 0 deletions src/node_util.cc
Original file line numberDiff line numberDiff line change
Expand Up@@ -18,15 +18,18 @@ using v8::CFunction;
using v8::Context;
using v8::DictionaryTemplate;
using v8::External;
using v8::Function;
using v8::FunctionCallbackInfo;
using v8::IndexFilter;
using v8::Integer;
using v8::Isolate;
using v8::KeyCollectionMode;
using v8::kPromiseHandlerAddedAfterReject;
using v8::Local;
using v8::LocalVector;
using v8::MaybeLocal;
using v8::Name;
using v8::Number;
using v8::Object;
using v8::ObjectTemplate;
using v8::ONLY_CONFIGURABLE;
Expand DownExpand Up@@ -467,6 +470,26 @@ void MarkPromiseAsHandled(const FunctionCallbackInfo<Value>& args) {
Local<Promise> promise = args[0].As<Promise>();
promise->MarkAsHandled();
promise->MarkAsSilent();

// If the promise is already rejected, then it may have already been
// reported to the unhandled rejection handler. Marking it as handled
// above does not trigger the v8 callback that updates it's status.
// So to avoid the notification we call out manually.
if (promise->State() == v8::Promise::kRejected) {
Environment* env = Environment::GetCurrent(args);
Local<Function> callback = env->promise_reject_callback();
CHECK(!callback.IsEmpty());

Local<Value> type =
Number::New(env->isolate(), kPromiseHandlerAddedAfterReject);
Local<Value> vargs[] = {type, promise, Undefined(env->isolate())};

USE(callback->Call(
env->context(), Undefined(env->isolate()), arraysize(vargs), vargs));

// Note that if callback->Call throws here, we go ahead and let that
// propagate.
}
}

void RegisterExternalReferences(ExternalReferenceRegistry* registry) {
Expand Down
25 changes: 25 additions & 0 deletions test/parallel/test-mark-promise-handled.js
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
'use strict';

const common = require('../common');
const assert = require('node:assert');
const { markPromiseAsHandled } = require('node:util');

process.on('unhandledrejection', common.mustNotCall());

markPromiseAsHandled(Promise.reject(123));

{
const { promise, reject } = Promise.withResolvers();
markPromiseAsHandled(promise);
reject(123);
}

{
const { promise, reject } = Promise.withResolvers();
reject(123);
markPromiseAsHandled(promise);
}

assert.throws(() => markPromiseAsHandled(123), {
code: 'ERR_INVALID_ARG_TYPE',
});
Loading