mirror of
https://github.com/denoland/deno.git
synced 2024-12-25 00:29:09 -05:00
Fix promise reject issue (#936)
This commit is contained in:
parent
c9f95d51da
commit
45d3b8955d
14 changed files with 299 additions and 6 deletions
1
BUILD.gn
1
BUILD.gn
|
@ -91,6 +91,7 @@ ts_sources = [
|
||||||
"js/os.ts",
|
"js/os.ts",
|
||||||
"js/platform.ts",
|
"js/platform.ts",
|
||||||
"js/plugins.d.ts",
|
"js/plugins.d.ts",
|
||||||
|
"js/promise_util.ts",
|
||||||
"js/read_dir.ts",
|
"js/read_dir.ts",
|
||||||
"js/read_file.ts",
|
"js/read_file.ts",
|
||||||
"js/read_link.ts",
|
"js/read_link.ts",
|
||||||
|
|
|
@ -3,6 +3,12 @@ import { globalEval } from "./global_eval";
|
||||||
|
|
||||||
// The libdeno functions are moved so that users can't access them.
|
// The libdeno functions are moved so that users can't access them.
|
||||||
type MessageCallback = (msg: Uint8Array) => void;
|
type MessageCallback = (msg: Uint8Array) => void;
|
||||||
|
export type PromiseRejectEvent =
|
||||||
|
| "RejectWithNoHandler"
|
||||||
|
| "HandlerAddedAfterReject"
|
||||||
|
| "ResolveAfterResolved"
|
||||||
|
| "RejectAfterResolved";
|
||||||
|
|
||||||
interface Libdeno {
|
interface Libdeno {
|
||||||
recv(cb: MessageCallback): void;
|
recv(cb: MessageCallback): void;
|
||||||
|
|
||||||
|
@ -20,6 +26,17 @@ interface Libdeno {
|
||||||
) => void
|
) => void
|
||||||
) => void;
|
) => void;
|
||||||
|
|
||||||
|
setPromiseRejectHandler: (
|
||||||
|
handler: (
|
||||||
|
error: Error | string,
|
||||||
|
event: PromiseRejectEvent,
|
||||||
|
/* tslint:disable-next-line:no-any */
|
||||||
|
promise: Promise<any>
|
||||||
|
) => void
|
||||||
|
) => void;
|
||||||
|
|
||||||
|
setPromiseErrorExaminer: (handler: () => boolean) => void;
|
||||||
|
|
||||||
mainSource: string;
|
mainSource: string;
|
||||||
mainSourceMap: RawSourceMap;
|
mainSourceMap: RawSourceMap;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import { DenoCompiler } from "./compiler";
|
||||||
import { libdeno } from "./libdeno";
|
import { libdeno } from "./libdeno";
|
||||||
import { args } from "./deno";
|
import { args } from "./deno";
|
||||||
import { sendSync, handleAsyncMsgFromRust } from "./dispatch";
|
import { sendSync, handleAsyncMsgFromRust } from "./dispatch";
|
||||||
|
import { promiseErrorExaminer, promiseRejectHandler } from "./promise_util";
|
||||||
|
|
||||||
function sendStart(): msg.StartRes {
|
function sendStart(): msg.StartRes {
|
||||||
const builder = new flatbuffers.Builder();
|
const builder = new flatbuffers.Builder();
|
||||||
|
@ -39,6 +40,8 @@ function onGlobalError(
|
||||||
export default function denoMain() {
|
export default function denoMain() {
|
||||||
libdeno.recv(handleAsyncMsgFromRust);
|
libdeno.recv(handleAsyncMsgFromRust);
|
||||||
libdeno.setGlobalErrorHandler(onGlobalError);
|
libdeno.setGlobalErrorHandler(onGlobalError);
|
||||||
|
libdeno.setPromiseRejectHandler(promiseRejectHandler);
|
||||||
|
libdeno.setPromiseErrorExaminer(promiseErrorExaminer);
|
||||||
const compiler = DenoCompiler.instance();
|
const compiler = DenoCompiler.instance();
|
||||||
|
|
||||||
// First we send an empty "Start" message to let the privileged side know we
|
// First we send an empty "Start" message to let the privileged side know we
|
||||||
|
|
46
js/promise_util.ts
Normal file
46
js/promise_util.ts
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
import { PromiseRejectEvent } from "./libdeno";
|
||||||
|
|
||||||
|
/* tslint:disable-next-line:no-any */
|
||||||
|
const rejectMap = new Map<Promise<any>, string>();
|
||||||
|
// For uncaught promise rejection errors
|
||||||
|
|
||||||
|
/* tslint:disable-next-line:no-any */
|
||||||
|
const otherErrorMap = new Map<Promise<any>, string>();
|
||||||
|
// For reject after resolve / resolve after resolve errors
|
||||||
|
|
||||||
|
export function promiseRejectHandler(
|
||||||
|
error: Error | string,
|
||||||
|
event: PromiseRejectEvent,
|
||||||
|
/* tslint:disable-next-line:no-any */
|
||||||
|
promise: Promise<any>
|
||||||
|
) {
|
||||||
|
switch (event) {
|
||||||
|
case "RejectWithNoHandler":
|
||||||
|
rejectMap.set(promise, (error as Error).stack || "RejectWithNoHandler");
|
||||||
|
break;
|
||||||
|
case "HandlerAddedAfterReject":
|
||||||
|
rejectMap.delete(promise);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// error is string here
|
||||||
|
otherErrorMap.set(promise, `Promise warning: ${error as string}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return true when continue, false to die on uncaught promise reject
|
||||||
|
export function promiseErrorExaminer(): boolean {
|
||||||
|
if (otherErrorMap.size > 0) {
|
||||||
|
for (const msg of otherErrorMap.values()) {
|
||||||
|
console.log(msg);
|
||||||
|
}
|
||||||
|
otherErrorMap.clear();
|
||||||
|
}
|
||||||
|
if (rejectMap.size > 0) {
|
||||||
|
for (const msg of rejectMap.values()) {
|
||||||
|
console.log(msg);
|
||||||
|
}
|
||||||
|
rejectMap.clear();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
|
@ -138,15 +138,59 @@ void HandleException(v8::Local<v8::Context> context,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExitOnPromiseRejectCallback(
|
const char* PromiseRejectStr(enum v8::PromiseRejectEvent e) {
|
||||||
v8::PromiseRejectMessage promise_reject_message) {
|
switch (e) {
|
||||||
|
case v8::PromiseRejectEvent::kPromiseRejectWithNoHandler:
|
||||||
|
return "RejectWithNoHandler";
|
||||||
|
case v8::PromiseRejectEvent::kPromiseHandlerAddedAfterReject:
|
||||||
|
return "HandlerAddedAfterReject";
|
||||||
|
case v8::PromiseRejectEvent::kPromiseResolveAfterResolved:
|
||||||
|
return "ResolveAfterResolved";
|
||||||
|
case v8::PromiseRejectEvent::kPromiseRejectAfterResolved:
|
||||||
|
return "RejectAfterResolved";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PromiseRejectCallback(v8::PromiseRejectMessage promise_reject_message) {
|
||||||
auto* isolate = v8::Isolate::GetCurrent();
|
auto* isolate = v8::Isolate::GetCurrent();
|
||||||
Deno* d = static_cast<Deno*>(isolate->GetData(0));
|
Deno* d = static_cast<Deno*>(isolate->GetData(0));
|
||||||
DCHECK_EQ(d->isolate, isolate);
|
DCHECK_EQ(d->isolate, isolate);
|
||||||
v8::HandleScope handle_scope(d->isolate);
|
v8::HandleScope handle_scope(d->isolate);
|
||||||
auto exception = promise_reject_message.GetValue();
|
auto exception = promise_reject_message.GetValue();
|
||||||
auto context = d->context.Get(d->isolate);
|
auto context = d->context.Get(d->isolate);
|
||||||
HandleException(context, exception);
|
auto promise = promise_reject_message.GetPromise();
|
||||||
|
auto event = promise_reject_message.GetEvent();
|
||||||
|
|
||||||
|
v8::Context::Scope context_scope(context);
|
||||||
|
auto promise_reject_handler = d->promise_reject_handler.Get(isolate);
|
||||||
|
|
||||||
|
if (!promise_reject_handler.IsEmpty()) {
|
||||||
|
v8::Local<v8::Value> args[3];
|
||||||
|
args[1] = v8_str(PromiseRejectStr(event));
|
||||||
|
args[2] = promise;
|
||||||
|
/* error, event, promise */
|
||||||
|
if (event == v8::PromiseRejectEvent::kPromiseRejectWithNoHandler) {
|
||||||
|
d->pending_promise_events++;
|
||||||
|
// exception only valid for kPromiseRejectWithNoHandler
|
||||||
|
args[0] = exception;
|
||||||
|
} else if (event ==
|
||||||
|
v8::PromiseRejectEvent::kPromiseHandlerAddedAfterReject) {
|
||||||
|
d->pending_promise_events--; // unhandled event cancelled
|
||||||
|
if (d->pending_promise_events < 0) {
|
||||||
|
d->pending_promise_events = 0;
|
||||||
|
}
|
||||||
|
// Placeholder, not actually used
|
||||||
|
args[0] = v8_str("Promise handler added");
|
||||||
|
} else if (event == v8::PromiseRejectEvent::kPromiseResolveAfterResolved) {
|
||||||
|
d->pending_promise_events++;
|
||||||
|
args[0] = v8_str("Promise resolved after resolved");
|
||||||
|
} else if (event == v8::PromiseRejectEvent::kPromiseRejectAfterResolved) {
|
||||||
|
d->pending_promise_events++;
|
||||||
|
args[0] = v8_str("Promise rejected after resolved");
|
||||||
|
}
|
||||||
|
promise_reject_handler->Call(context->Global(), 3, args);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Print(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
void Print(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||||
|
@ -279,6 +323,48 @@ void SetGlobalErrorHandler(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||||
d->global_error_handler.Reset(isolate, func);
|
d->global_error_handler.Reset(isolate, func);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sets the promise uncaught reject handler
|
||||||
|
void SetPromiseRejectHandler(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||||
|
v8::Isolate* isolate = args.GetIsolate();
|
||||||
|
Deno* d = reinterpret_cast<Deno*>(isolate->GetData(0));
|
||||||
|
DCHECK_EQ(d->isolate, isolate);
|
||||||
|
|
||||||
|
v8::HandleScope handle_scope(isolate);
|
||||||
|
|
||||||
|
if (!d->promise_reject_handler.IsEmpty()) {
|
||||||
|
isolate->ThrowException(
|
||||||
|
v8_str("libdeno.setPromiseRejectHandler already called."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
v8::Local<v8::Value> v = args[0];
|
||||||
|
CHECK(v->IsFunction());
|
||||||
|
v8::Local<v8::Function> func = v8::Local<v8::Function>::Cast(v);
|
||||||
|
|
||||||
|
d->promise_reject_handler.Reset(isolate, func);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sets the promise uncaught reject handler
|
||||||
|
void SetPromiseErrorExaminer(const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||||
|
v8::Isolate* isolate = args.GetIsolate();
|
||||||
|
Deno* d = reinterpret_cast<Deno*>(isolate->GetData(0));
|
||||||
|
DCHECK_EQ(d->isolate, isolate);
|
||||||
|
|
||||||
|
v8::HandleScope handle_scope(isolate);
|
||||||
|
|
||||||
|
if (!d->promise_error_examiner.IsEmpty()) {
|
||||||
|
isolate->ThrowException(
|
||||||
|
v8_str("libdeno.setPromiseErrorExaminer already called."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
v8::Local<v8::Value> v = args[0];
|
||||||
|
CHECK(v->IsFunction());
|
||||||
|
v8::Local<v8::Function> func = v8::Local<v8::Function>::Cast(v);
|
||||||
|
|
||||||
|
d->promise_error_examiner.Reset(isolate, func);
|
||||||
|
}
|
||||||
|
|
||||||
bool ExecuteV8StringSource(v8::Local<v8::Context> context,
|
bool ExecuteV8StringSource(v8::Local<v8::Context> context,
|
||||||
const char* js_filename,
|
const char* js_filename,
|
||||||
v8::Local<v8::String> source) {
|
v8::Local<v8::String> source) {
|
||||||
|
@ -354,6 +440,24 @@ void InitializeContext(v8::Isolate* isolate, v8::Local<v8::Context> context,
|
||||||
set_global_error_handler_val)
|
set_global_error_handler_val)
|
||||||
.FromJust());
|
.FromJust());
|
||||||
|
|
||||||
|
auto set_promise_reject_handler_tmpl =
|
||||||
|
v8::FunctionTemplate::New(isolate, SetPromiseRejectHandler);
|
||||||
|
auto set_promise_reject_handler_val =
|
||||||
|
set_promise_reject_handler_tmpl->GetFunction(context).ToLocalChecked();
|
||||||
|
CHECK(deno_val
|
||||||
|
->Set(context, deno::v8_str("setPromiseRejectHandler"),
|
||||||
|
set_promise_reject_handler_val)
|
||||||
|
.FromJust());
|
||||||
|
|
||||||
|
auto set_promise_error_examiner_tmpl =
|
||||||
|
v8::FunctionTemplate::New(isolate, SetPromiseErrorExaminer);
|
||||||
|
auto set_promise_error_examiner_val =
|
||||||
|
set_promise_error_examiner_tmpl->GetFunction(context).ToLocalChecked();
|
||||||
|
CHECK(deno_val
|
||||||
|
->Set(context, deno::v8_str("setPromiseErrorExaminer"),
|
||||||
|
set_promise_error_examiner_val)
|
||||||
|
.FromJust());
|
||||||
|
|
||||||
{
|
{
|
||||||
auto source = deno::v8_str(js_source.c_str());
|
auto source = deno::v8_str(js_source.c_str());
|
||||||
CHECK(
|
CHECK(
|
||||||
|
@ -389,6 +493,7 @@ void InitializeContext(v8::Isolate* isolate, v8::Local<v8::Context> context,
|
||||||
}
|
}
|
||||||
|
|
||||||
void AddIsolate(Deno* d, v8::Isolate* isolate) {
|
void AddIsolate(Deno* d, v8::Isolate* isolate) {
|
||||||
|
d->pending_promise_events = 0;
|
||||||
d->next_req_id = 0;
|
d->next_req_id = 0;
|
||||||
d->isolate = isolate;
|
d->isolate = isolate;
|
||||||
// Leaving this code here because it will probably be useful later on, but
|
// Leaving this code here because it will probably be useful later on, but
|
||||||
|
@ -397,7 +502,7 @@ void AddIsolate(Deno* d, v8::Isolate* isolate) {
|
||||||
// d->isolate->SetAbortOnUncaughtExceptionCallback(AbortOnUncaughtExceptionCallback);
|
// d->isolate->SetAbortOnUncaughtExceptionCallback(AbortOnUncaughtExceptionCallback);
|
||||||
// d->isolate->AddMessageListener(MessageCallback2);
|
// d->isolate->AddMessageListener(MessageCallback2);
|
||||||
// d->isolate->SetFatalErrorHandler(FatalErrorCallback2);
|
// d->isolate->SetFatalErrorHandler(FatalErrorCallback2);
|
||||||
d->isolate->SetPromiseRejectCallback(deno::ExitOnPromiseRejectCallback);
|
d->isolate->SetPromiseRejectCallback(deno::PromiseRejectCallback);
|
||||||
d->isolate->SetData(0, d);
|
d->isolate->SetData(0, d);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -490,6 +595,36 @@ int deno_respond(Deno* d, void* user_data, int32_t req_id, deno_buf buf) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void deno_check_promise_errors(Deno* d) {
|
||||||
|
if (d->pending_promise_events > 0) {
|
||||||
|
auto* isolate = d->isolate;
|
||||||
|
v8::Locker locker(isolate);
|
||||||
|
v8::Isolate::Scope isolate_scope(isolate);
|
||||||
|
v8::HandleScope handle_scope(isolate);
|
||||||
|
|
||||||
|
auto context = d->context.Get(d->isolate);
|
||||||
|
v8::Context::Scope context_scope(context);
|
||||||
|
|
||||||
|
v8::TryCatch try_catch(d->isolate);
|
||||||
|
auto promise_error_examiner = d->promise_error_examiner.Get(d->isolate);
|
||||||
|
if (promise_error_examiner.IsEmpty()) {
|
||||||
|
d->last_exception =
|
||||||
|
"libdeno.setPromiseErrorExaminer has not been called.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
v8::Local<v8::Value> args[0];
|
||||||
|
auto result = promise_error_examiner->Call(context->Global(), 0, args);
|
||||||
|
if (try_catch.HasCaught()) {
|
||||||
|
deno::HandleException(context, try_catch.Exception());
|
||||||
|
}
|
||||||
|
d->pending_promise_events = 0; // reset
|
||||||
|
if (!result->BooleanValue(context).FromJust()) {
|
||||||
|
// Has uncaught promise reject error, exiting...
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void deno_delete(Deno* d) {
|
void deno_delete(Deno* d) {
|
||||||
d->isolate->Dispose();
|
d->isolate->Dispose();
|
||||||
delete d;
|
delete d;
|
||||||
|
|
|
@ -59,6 +59,8 @@ int deno_execute(Deno* d, void* user_data, const char* js_filename,
|
||||||
// libdeno.recv() callback. Check deno_last_exception() for exception text.
|
// libdeno.recv() callback. Check deno_last_exception() for exception text.
|
||||||
int deno_respond(Deno* d, void* user_data, int32_t req_id, deno_buf buf);
|
int deno_respond(Deno* d, void* user_data, int32_t req_id, deno_buf buf);
|
||||||
|
|
||||||
|
void deno_check_promise_errors(Deno* d);
|
||||||
|
|
||||||
const char* deno_last_exception(Deno* d);
|
const char* deno_last_exception(Deno* d);
|
||||||
|
|
||||||
void deno_terminate_execution(Deno* d);
|
void deno_terminate_execution(Deno* d);
|
||||||
|
|
|
@ -14,6 +14,9 @@ struct deno_s {
|
||||||
std::string last_exception;
|
std::string last_exception;
|
||||||
v8::Persistent<v8::Function> recv;
|
v8::Persistent<v8::Function> recv;
|
||||||
v8::Persistent<v8::Function> global_error_handler;
|
v8::Persistent<v8::Function> global_error_handler;
|
||||||
|
v8::Persistent<v8::Function> promise_reject_handler;
|
||||||
|
v8::Persistent<v8::Function> promise_error_examiner;
|
||||||
|
int32_t pending_promise_events;
|
||||||
v8::Persistent<v8::Context> context;
|
v8::Persistent<v8::Context> context;
|
||||||
v8::Persistent<v8::Map> async_data_map;
|
v8::Persistent<v8::Map> async_data_map;
|
||||||
deno_recv_cb cb;
|
deno_recv_cb cb;
|
||||||
|
@ -32,10 +35,16 @@ void Print(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||||
void Recv(const v8::FunctionCallbackInfo<v8::Value>& args);
|
void Recv(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||||
void Send(const v8::FunctionCallbackInfo<v8::Value>& args);
|
void Send(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||||
void SetGlobalErrorHandler(const v8::FunctionCallbackInfo<v8::Value>& args);
|
void SetGlobalErrorHandler(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||||
|
void SetPromiseRejectHandler(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||||
|
void SetPromiseErrorExaminer(const v8::FunctionCallbackInfo<v8::Value>& args);
|
||||||
static intptr_t external_references[] = {
|
static intptr_t external_references[] = {
|
||||||
reinterpret_cast<intptr_t>(Print), reinterpret_cast<intptr_t>(Recv),
|
reinterpret_cast<intptr_t>(Print),
|
||||||
|
reinterpret_cast<intptr_t>(Recv),
|
||||||
reinterpret_cast<intptr_t>(Send),
|
reinterpret_cast<intptr_t>(Send),
|
||||||
reinterpret_cast<intptr_t>(SetGlobalErrorHandler), 0};
|
reinterpret_cast<intptr_t>(SetGlobalErrorHandler),
|
||||||
|
reinterpret_cast<intptr_t>(SetPromiseRejectHandler),
|
||||||
|
reinterpret_cast<intptr_t>(SetPromiseErrorExaminer),
|
||||||
|
0};
|
||||||
|
|
||||||
Deno* NewFromSnapshot(void* user_data, deno_recv_cb cb);
|
Deno* NewFromSnapshot(void* user_data, deno_recv_cb cb);
|
||||||
|
|
||||||
|
|
|
@ -193,3 +193,15 @@ TEST(LibDenoTest, DataBuf) {
|
||||||
EXPECT_EQ(data_buf_copy.data_ptr[1], 8);
|
EXPECT_EQ(data_buf_copy.data_ptr[1], 8);
|
||||||
deno_delete(d);
|
deno_delete(d);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(LibDenoTest, PromiseRejectCatchHandling) {
|
||||||
|
static int count = 0;
|
||||||
|
Deno* d = deno_new([](auto _, int req_id, auto buf, auto data_buf) {
|
||||||
|
// If no error, nothing should be sent, and count should not increment
|
||||||
|
count++;
|
||||||
|
});
|
||||||
|
EXPECT_TRUE(deno_execute(d, nullptr, "a.js", "PromiseRejectCatchHandling()"));
|
||||||
|
|
||||||
|
EXPECT_EQ(count, 0);
|
||||||
|
deno_delete(d);
|
||||||
|
}
|
||||||
|
|
|
@ -133,3 +133,43 @@ global.DataBuf = () => {
|
||||||
b[0] = 9;
|
b[0] = 9;
|
||||||
b[1] = 8;
|
b[1] = 8;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
global.PromiseRejectCatchHandling = () => {
|
||||||
|
let count = 0;
|
||||||
|
let promiseRef = null;
|
||||||
|
// When we have an error, libdeno sends something
|
||||||
|
function assertOrSend(cond) {
|
||||||
|
if (!cond) {
|
||||||
|
libdeno.send(new Uint8Array([42]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
libdeno.setPromiseErrorExaminer(() => {
|
||||||
|
assertOrSend(count === 2);
|
||||||
|
});
|
||||||
|
libdeno.setPromiseRejectHandler((error, event, promise) => {
|
||||||
|
count++;
|
||||||
|
if (event === "RejectWithNoHandler") {
|
||||||
|
assertOrSend(error instanceof Error);
|
||||||
|
assertOrSend(error.message === "message");
|
||||||
|
assertOrSend(count === 1);
|
||||||
|
promiseRef = promise;
|
||||||
|
} else if (event === "HandlerAddedAfterReject") {
|
||||||
|
assertOrSend(count === 2);
|
||||||
|
assertOrSend(promiseRef === promise);
|
||||||
|
}
|
||||||
|
// Should never reach 3!
|
||||||
|
assertOrSend(count !== 3);
|
||||||
|
});
|
||||||
|
|
||||||
|
async function fn() {
|
||||||
|
throw new Error("message");
|
||||||
|
}
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
try {
|
||||||
|
await fn();
|
||||||
|
} catch (e) {
|
||||||
|
assertOrSend(count === 2);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
|
|
@ -195,6 +195,12 @@ impl Isolate {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_promise_errors(&self) {
|
||||||
|
unsafe {
|
||||||
|
libdeno::deno_check_promise_errors(self.libdeno_isolate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO Use Park abstraction? Note at time of writing Tokio default runtime
|
// TODO Use Park abstraction? Note at time of writing Tokio default runtime
|
||||||
// does not have new_with_park().
|
// does not have new_with_park().
|
||||||
pub fn event_loop(&mut self) {
|
pub fn event_loop(&mut self) {
|
||||||
|
@ -205,7 +211,10 @@ impl Isolate {
|
||||||
Err(mpsc::RecvTimeoutError::Timeout) => self.timeout(),
|
Err(mpsc::RecvTimeoutError::Timeout) => self.timeout(),
|
||||||
Err(e) => panic!("recv_deadline() failed: {:?}", e),
|
Err(e) => panic!("recv_deadline() failed: {:?}", e),
|
||||||
}
|
}
|
||||||
|
self.check_promise_errors();
|
||||||
}
|
}
|
||||||
|
// Check on done
|
||||||
|
self.check_promise_errors();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ntasks_increment(&mut self) {
|
fn ntasks_increment(&mut self) {
|
||||||
|
|
|
@ -33,6 +33,7 @@ extern "C" {
|
||||||
pub fn deno_new(cb: DenoRecvCb) -> *const isolate;
|
pub fn deno_new(cb: DenoRecvCb) -> *const isolate;
|
||||||
pub fn deno_delete(i: *const isolate);
|
pub fn deno_delete(i: *const isolate);
|
||||||
pub fn deno_last_exception(i: *const isolate) -> *const c_char;
|
pub fn deno_last_exception(i: *const isolate) -> *const c_char;
|
||||||
|
pub fn deno_check_promise_errors(i: *const isolate);
|
||||||
pub fn deno_respond(
|
pub fn deno_respond(
|
||||||
i: *const isolate,
|
i: *const isolate,
|
||||||
user_data: *mut c_void,
|
user_data: *mut c_void,
|
||||||
|
|
14
tests/018_async_catch.ts
Normal file
14
tests/018_async_catch.ts
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
async function fn() {
|
||||||
|
throw new Error("message");
|
||||||
|
}
|
||||||
|
async function call() {
|
||||||
|
try {
|
||||||
|
console.log("before await fn()");
|
||||||
|
await fn();
|
||||||
|
console.log("after await fn()");
|
||||||
|
} catch (error) {
|
||||||
|
console.log("catch");
|
||||||
|
}
|
||||||
|
console.log("after try-catch");
|
||||||
|
}
|
||||||
|
call().catch(() => console.log("outer catch"));
|
3
tests/018_async_catch.ts.out
Normal file
3
tests/018_async_catch.ts.out
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
before await fn()
|
||||||
|
catch
|
||||||
|
after try-catch
|
|
@ -1,5 +1,6 @@
|
||||||
hello
|
hello
|
||||||
before error
|
before error
|
||||||
|
world
|
||||||
Error: error
|
Error: error
|
||||||
at foo ([WILDCARD]tests/async_error.ts:4:9)
|
at foo ([WILDCARD]tests/async_error.ts:4:9)
|
||||||
at eval ([WILDCARD]tests/async_error.ts:7:1)
|
at eval ([WILDCARD]tests/async_error.ts:7:1)
|
||||||
|
|
Loading…
Reference in a new issue