侧边栏壁纸
  • 累计撰写 218 篇文章
  • 累计创建 59 个标签
  • 累计收到 5 条评论

NAPI 笔记 09:C++ 和 JavaScript 之间交互时的异常处理

barwe
2022-08-31 / 0 评论 / 0 点赞 / 1,974 阅读 / 3,014 字
温馨提示:
本文最后更新于 2023-06-07,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

Introduction

这里的 异常处理 指的是 node-addon-api 如何在 C++ 与 JavaScript 之间进行异常的交互,可以分为:

  • C++ 中向 JavaScript 抛出一个异常,异常应由 JavaScript 捕获或者继续冒泡
  • C++ 中调用 JavaScript 函数时怎么处理 JavaScript 函数抛出的 JavaScript 异常
    • 直接处理 JavaScript 异常
    • 向外冒泡异常(将异常再次抛给 JavaScript)

整体上,node-addon-api 提供了两种方式用于解决 C++ 和 JavaScript 之间的异常交互问题。

启用 C++ 异常处理机制时,C++ 可以通过 throw 关键字向 JavaScript 抛出一个异常,通过 try/catch 捕获 JavaScript 函数抛出的异常。

这里涉及到了两种异常对象(JavaScript 异常和 C++ Napi::Error异常)之间的转换问题,这种转换是自动进行的。

禁用 C++ 异常处理机制时,C++ 不能通过 throw 向 JavaScript 抛出异常了,也不能通过 try/catch 捕获 JavaScript 函数抛出的异常。

注意,是不能使用 throw / try / catch 处理 C++ 和 JavaScript 之间的异常交互,对 C++ 内部的异常处理没有影响。

此时,node-addon-api 通过将异常信息保存在返回值中返回给 JavaScript,或者通过检测 env 对象的异常状态来判断执行 JavaScript 函数时是否发生异常。

上述异常处理机制又涉及到两种方式:基于Value对象的,和将返回的Value对象打包成Maybe对象的检测。

综上,node-addon-api 处理 C++ 和 JavaScript 异常交互的方式有:

  • 启用 C++ 异常时:throw / try / catch
  • 禁用 C++ 异常时:returned Value object
    • check Value value
    • check Maybe value (boxed Value value)

下面分别举例:

Enable C++ exceptions

此时Napi::Error类将从std::exception继承。

向 JavaScript 抛出异常

与一般异常处理逻辑相同,通过 throw 关键字抛出异常:

throw Error::New(env, "error message");

处理 JavaScript 函数抛出的异常

C++ 中调用 JavaScript 函数,函数执行过程中可能会抛出异常。

与一般异常处理逻辑相同,通过 try/catch 处理异常,JavaScript 函数抛出的异常会被自动转换成Error对象。

// 执行 js 函数前需要先将其转换为 Function 对象
Function jsFunc = someValue.As<Function>();

try {
    Value result = jsFunc({ arg1, arg2 });
} catch (const Error& e) {
    // 通过`Error`对象的`what()`方法获取异常消息
    cerr << "JavaScript exception: " + e.what();
}

抛出 JavaScript 函数抛出的异常

如果需要将 JavaScript 函数抛出的异常重新抛回给 JavaScript,那么啥也不需要做,异常会自己冒泡:

Value result = jsFunc({ arg1, arg2 });

Disable C++ exceptions

此时,Napi::Error不会从std::excaption继承。

不能使用 throw 向 JavaScript 抛出异常,也不能通过 try/catch 捕获 JavaScript 抛出的异常。

向 JavaScript 抛出异常

node-addon-api 为Error对象实现了一个ThrowAsJavaScriptException()方法用于直接向 JavaScript 抛出异常:

Error::New(env, "error message").ThrowAsJavaScriptException();

处理 JavaScript 函数抛出的异常

node-addon-api 通过检测 JavaScript 函数的返回值或者环境对象来判断 JavaScript 函数执行是否发生异常。

检测 Value 对象

默认情况下,JavaScript 函数执行结束后返回一个Value实例:

Value result = jsFunc();

通过env.IsExceptionPending()可以检测 JavaScript 函数执行过程中是否发生异常。

通过env.GetAndClearPendingException()取回异常对象并重置全局异常记录变量的状态。

// JavaScript 函数抛出了异常
if (env.IsExceptionPending()) {
    Error e = env.GetAndClearPendingException();
    cerr << "JavaScript exception: " + e.what();
}

检测 Maybe 对象

Maybe是对基于返回值进行异常检测的简单包装。

Maybe<Value> maybeResult = jsFunc();
if (maybeResult.IsNothing()) {
    Error e = env.GetAndClearPendingException();
    cerr << "JavaScript exception: " + e.what();
}

抛出 JavaScript 函数抛出的异常

与直接处理异常类似。

检测 Value 对象

需要从环境对象中取回并重置异常,然后返回异常的内容。

实际上是将异常当做返回值在处理。

Value v = jsFunc();
if (v.IsExceptionPending()) {
    Error e = env.GetAndClearPendingException();
    return e.Value();
}

检测 Maybe 对象

相对于检测Value实例来说,更加简洁。

Maybe<Value> mv = jsFunc();
Value v;
if (!mv.To(&v)) {
    return v;
}

Macros

node-addon-api 提供了几个宏用于抛出异常,开发者不需要关系 C++ 异常机制是否被禁用了。

NAPI_THROW(error, ...)

抛出一个 Error 对象,同时返回一个值。如果启用了 C++ 异常,返回的值会被忽略。

NAPI_THROW_IF_FAILED(env, status, ...)

指定一个 napi_status 自动构建一个 Error 对象,同时返回一个值。如果启用了 C++ 异常,返回的值会被忽略。

NAPI_THROW_IF_FAILED_VOID(env, status)

指定一个 napi_status 自动构建一个 Error 对象,同时返回。如果启用了 C++ 异常,返回的值会被忽略。

NAPI_FATAL_IF_FAILED(status, location, message)

指定一个 napi_status,一个字符串表示出错的位置,一个展示错误消息的字符串。

Reference

0

评论区