JSI入门指南

JSI入门指南

前言

要想彻底理解React Native新架构,JSI是绕不过去的槛。所以本文作为React Native源码剖析的第二篇文章。阅读本文需要了解一些简单的现代C++知识。新架构RN基本上是基于C++ 17开发的。学习现代C++可以极大的扩宽知识边界,提升能力范围。而且现代C++更加规范,学习难度降低,是性价比极高的选择。当然,仅作为源码阅读的需要,并不用深入学习C++,甚至不用会写代码,只要能看懂一些语法足够了。如果想更进一步,学习基本的现代C++开发,可以帮助我们开发React Native的纯C++ TurboModule,可以极大的优化RN App的性能,以及应用范围,在后面的RN新架构的一些列介绍文章中,相信对这一点一定体会极深。

JSI 概述

什么是JSI?

JSI(JavaScript Interface) 是 React Native 新架构中的一个核心组件,本质上是一个由 C++ 实现的轻量级接口层,用来连接:

  • 一边是 JavaScript 运行时(如 Hermes、V8)
  • 另一边是原生代码(主要是 C++,间接连到 Java/Obj‑C/平台 API)

它的目标是取代旧的异步 Bridge(JSON 消息桥) ,让 JS 与原生之间可以 直接、高性能地互相调用,大幅降低通信开销和延迟。

简单说,以前 JS 和原生要靠发 JSON 消息来通信,现在通过 JSI,双方可以像直接函数调用那样对话,并且可以互相保存对方对象的引用。

JSI 的核心特点

  1. 直接、低开销的 JS ↔ 原生通信

    • JSI 允许 JavaScript 保存 C++ 对象引用,C++ 也能保存 JS 对象引用,通过内存引用直接调用方法

    • 不再需要把数据转成 JSON 再跨线程传递,去掉序列化/反序列化开销,尤其对大数据量(如相机帧缓冲、图像、音频样本等)非常关键

    • 性能密集型库(例如 VisionCamera、Reanimated、Skia 等)借助 JSI 才能在 React Native 里做到接近原生的实时性能

  2. 支持同步和异步调用

    • 旧 Bridge 只能异步,很多场景需要"立刻拿到结果"很别扭。

    • JSI 提供:

      • 异步调用:常规推荐方式,避免阻塞 JS 线程或 UI 线程。

      • 同步调用:在确实需要"立即返回值"的场景(例如获取剪贴板、当前位置等)可以直接从原生拿值返回给 JS,而不用 Promise / 回调

      • 这种"既支持同步又支持异步"的模式,让 React Native 在易用性和性能之间有了更多空间。

  3. 用 C++ 实现、为性能而生

    • JSI 自身是 C++ API ,提供 jsi::Runtimejsi::Objectjsi::Functionjsi::Value 等低层接口,用于:

      • 在原生代码中操作 JS 值与对象;
      • 注册可供 JS 调用的原生函数;
      • 直接与 JS 引擎交互
      • 由于是本地编译代码,减少了中间层和解释开销,带来更好的 启动时间与运行时性能
  4. 脱离旧 Bridge,成为新架构的基础。React Native 新架构的几个关键词:JSI、TurboModules、Fabric、Codegen,其中 JSI 是整个系统的"底座"

    • TurboModules:新一代原生模块系统,通过 JSI 实现 JS 与原生模块的直接调用,而不是通过老 Bridge

    • Fabric 渲染器:UI 事件与渲染更新通过 JSI 与 JS 运行时通信,使得界面更新更高效、更可控

    • Codegen:从类型化的 JS/TS 声明自动生成原生 C++/平台代码,这些代码通过 JSI 与 JS 通信

    • JSI 还是一个 与 JS 引擎无关的接口层,因此可以支持 Hermes、V8 等多种引擎,不再绑定于 JavaScriptCore

  5. 更适合高性能与跨平台原生模块。借助 JSI,你可以:

    • 纯 C++ 模块,然后在 Android 和 iOS 上复用这一套实现,只需很少的平台胶水代码
    • 直接在原生层访问设备能力(相机、蓝牙、GPS 等),并暴露给 JS 使用,同时保持高性能
    • 将复杂实例对象(数据库连接、图像缓冲、音频样本等)直接暴露给 JS 层,而不是一堆 JSON 数据
JSI vs Bridge
特性 Bridge (旧架构) JSI (新架构)
通信方式 JSON 序列化/反序列化 直接内存访问
性能 慢(每次调用都序列化) 快(零序列化开销)
同步调用 ❌ 不支持 ✅ 支持
类型安全 ❌ 运行时检查 ✅ C++ 类型系统
内存开销 高(JSON 字符串) 低(直接引用)

JSI 类型系统

类型层级结构
typescript 复制代码
ICast (接口)
  └─ Runtime (抽象类)

Pointer (基类 - 不可拷贝,可移动)
  ├─ PropNameID (属性名)
  ├─ Symbol (ES6 Symbol)
  ├─ BigInt (大整数)
  ├─ String (字符串)
  └─ Object (对象)
       ├─ Array (数组)
       ├─ ArrayBuffer (二进制缓冲区)
       └─ Function (函数)

Value (联合类型 - 可存储任意 JS 值)
  ├─ undefined
  ├─ null
  ├─ boolean
  ├─ number
  ├─ Symbol
  ├─ BigInt
  ├─ String
  └─ Object
Runtime - JS 引擎抽象
cpp 复制代码
class Runtime : public ICast {
 public:
  // 1. 执行 JavaScript 代码
  virtual Value evaluateJavaScript(
      const std::shared_ptr<const Buffer>& buffer,
      const std::string& sourceURL) = 0;

  // 2. 创建 JS 对象
  virtual Object createObject() = 0;
  virtual Array createArray(size_t length) = 0;
  virtual Function createFunctionFromHostFunction(
      const PropNameID& name,
      unsigned int paramCount,
      HostFunctionType func) = 0;

  // 3. 访问全局对象
  virtual Object global() = 0;

  // 4. 属性操作
  virtual Value getProperty(const Object&, const PropNameID& name) = 0;
  virtual void setPropertyValue(
      const Object&, const PropNameID& name, const Value& value) = 0;

  // 5. 微任务队列管理
  virtual void queueMicrotask(const Function& callback) = 0;
  virtual bool drainMicrotasks(int maxMicrotasksHint = -1) = 0;
};

引擎的具体实现(如:Hermes、JSC、V8)

cpp 复制代码
// Hermes 实现示例
class HermesRuntime : public Runtime {
 public:
  Value evaluateJavaScript(...) override {
    // Hermes 特定的 JS 执行逻辑
  }

  Object createObject() override {
    // 调用 Hermes API 创建对象
  }
};
Value - 通用 JS 值类型
cpp 复制代码
class Value {
 private:
  enum ValueKind {
    UndefinedKind,
    NullKind,
    BooleanKind,
    NumberKind,
    SymbolKind,
    BigIntKind,
    StringKind,
    ObjectKind,
  };

  union Data {
    bool boolean;
    double number;
    Pointer pointer;  // Symbol/String/Object
  };

  ValueKind kind_;
  Data data_;  // 8 字节(64 位)
};

类型检查与转换

cpp 复制代码
void processValue(Runtime& runtime, const Value& value) {
  if (value.isUndefined()) {
    // 处理 undefined
  } else if (value.isNull()) {
    // 处理 null
  } else if (value.isBool()) {
    bool b = value.getBool();  // 断言检查
    bool b2 = value.asBool();  // 抛出异常
  } else if (value.isNumber()) {
    double d = value.getNumber();
  } else if (value.isString()) {
    String str = value.getString(runtime);
    std::string utf8 = str.utf8(runtime);
  } else if (value.isObject()) {
    Object obj = value.getObject(runtime);
  }
}
Object - JS 对象

创建对象

cpp 复制代码
// 1. 空对象
Object obj(runtime);  // 等价于 JS: {}

// 2. 带原型的对象
Value proto = runtime.global().getProperty(runtime, "MyPrototype");
Object obj = Object::create(runtime, proto);

// 3. HostObject(C++ 对象)
class MyHostObject : public HostObject {
  Value get(Runtime& rt, const PropNameID& name) override {
    if (name.utf8(rt) == "value") {
      return Value(42);
    }
    return Value::undefined();
  }
};

auto ho = std::make_shared<MyHostObject>();
Object obj = Object::createFromHostObject(runtime, ho);

属性操作

cpp 复制代码
Object obj(runtime);

// 设置属性(支持多种类型)
obj.setProperty(runtime, "name", "John");  // const char*
obj.setProperty(runtime, "age", 30);       // int
obj.setProperty(runtime, "active", true);  // bool

// 获取属性
Value name = obj.getProperty(runtime, "name");
if (name.isString()) {
  std::string str = name.getString(runtime).utf8(runtime);
}

// 检查属性
if (obj.hasProperty(runtime, "age")) {
  // 属性存在
}

// 删除属性
obj.deleteProperty(runtime, "age");
Function - JS 函数

创建 C++ 函数供 JS 调用

cpp 复制代码
// 定义 C++ lambda函数
auto myFunc = [](Runtime& runtime,
                  const Value& thisVal,
                  const Value* args,
                  size_t count) -> Value {
  // 参数校验
  if (count < 2) {
    throw JSError(runtime, "Expected 2 arguments");
  }

  if (!args[0].isNumber() || !args[1].isNumber()) {
    throw JSError(runtime, "Arguments must be numbers");
  }

  // 执行逻辑
  double sum = args[0].getNumber() + args[1].getNumber();
  return Value(sum);
};

// 注册到全局对象
auto funcName = PropNameID::forAscii(runtime, "myAdd");
Function func = Function::createFromHostFunction(
    runtime, funcName, 2, myFunc);

runtime.global().setProperty(runtime, "myAdd", func);

// JS 中调用:
// const result = myAdd(10, 20);  // 30

从 C++ 调用 JS 函数

cpp 复制代码
// 获取 JS 函数
Value callback = obj.getProperty(runtime, "onClick");
if (callback.isObject() && callback.getObject(runtime).isFunction(runtime)) {
  Function func = callback.getObject(runtime).getFunction(runtime);

  // 方式 1:无 this,传递参数
  Value result = func.call(runtime, Value(10), Value(20));

  // 方式 2:带 this 上下文
  Object thisObj(runtime);
  Value result2 = func.callWithThis(runtime, thisObj, Value(10));

  // 方式 3:作为构造函数调用
  Value instance = func.callAsConstructor(runtime, Value("arg1"));
}
Array - JS 数组
cpp 复制代码
// 创建数组
Array arr = Array::createWithElements(runtime, 1, 2, "hello", true);

// 访问元素
size_t length = arr.size(runtime);
for (size_t i = 0; i < length; i++) {
  Value element = arr.getValueAtIndex(runtime, i);
}

// 修改元素
arr.setValueAtIndex(runtime, 0, Value(100));

// 转换为普通 Object
Object obj = arr.asObject(runtime);  // 类型安全转换

JS与C++的调用机制

HostObject

HostObject是一个非常重要的概念,它的作用就是将一个C++对象直接暴露给JS层使用。

更具体的说:

  • HostObject 是一个 C++ 类 ,完整的类是 facebook::jsi::HostObject
  • 你可以通过它把原生对象(例如图片、存储、数据库连接等)暴露给 JS
  • JS 访问它的属性和方法时,看起来就像在用普通的 JS 对象

如何使用HostObject

1.在 C++ 中定义 HostObject:
cpp 复制代码
class NativeStorage : public facebook::jsi::HostObject {
public:
  int expirationTime = 60 * 60 * 24; // 默认 1 天

  // 读属性:nativeStorage.xxx
  jsi::Value get(jsi::Runtime& runtime, const jsi::PropNameID& name) override {
    auto prop = name.utf8(runtime);
    if (prop == "expirationTime") {
      return jsi::Value(expirationTime);
    }

    // 也可以在这里返回"方法",例如 setObject / object(见后文)
    // 否则:
    return jsi::Value::undefined();
  }

  // 写属性:nativeStorage.xxx = ...
  void set(jsi::Runtime& runtime, const jsi::PropNameID& name, const jsi::Value& value) override {
    auto prop = name.utf8(runtime);
    if (prop == "expirationTime" && value.isNumber()) {
      expirationTime = (int)value.asNumber();
    }
  }
};

这里get方法相当于在定义:JS 中访问某个属性名时,底层到底要操作哪个 C++ 字段或执行什么逻辑

2.把 HostObject 实例挂到 JS 运行时
cpp 复制代码
void NativeStorage::install(jsi::Runtime& runtime) {
  // 创建NativeStorage对象
  auto instance = std::make_shared<NativeStorage>();

  // 再从 HostObject 创建 JS 对象
  auto object = jsi::Object::createFromHostObject(runtime, instance);

  // 挂到 global 上,供 JS 使用:global.nativeStorage
  runtime.global().setProperty(runtime, "nativeStorage", object);
}

这个过程可以称为安装,在适当的时机(通常是 JS runtime 已经创建好之后),调用这个 install 就行。

3.在 JS 侧使用
js 复制代码
// 属性读写:对应 C++ 中 get/set 覆写
nativeStorage.expirationTime = 1000;
console.log(nativeStorage.expirationTime);  // -> 1000

从 JS 视角看,这就是一个普通对象;从 C++ 视角看,它是一个持有原生资源和逻辑的类实例。

4.添加方法

HostObject 不只可以暴露数据属性,还可以在 get() 中给某个属性名返回一个 HostFunction ,这样这个属性在 JS 中就是一个 可调用方法

例如在上面 NativeStorage 的基础上,给它加上 setObject / object 方法:

cpp 复制代码
jsi::Value get(jsi::Runtime& runtime, const jsi::PropNameID& name) override {
  auto prop = name.utf8(runtime);

  if (prop == "expirationTime") {
    return jsi::Value(expirationTime);
  }

  if (prop == "setObject") {
    return jsi::Function::createFromHostFunction(
      runtime,
      jsi::PropNameID::forAscii(runtime, "setObject"),
      2, // 参数个数:key, value
      [](jsi::Runtime& rt, const jsi::Value& thisVal,
         const jsi::Value* args, size_t count) -> jsi::Value {
        // 这里做参数转换 + 原生存储逻辑
        // 比如用 NSUserDefaults 或 SharedPreferences 等
        return jsi::Value(true);
      }
    );
  }

  if (prop == "object") {
    return jsi::Function::createFromHostFunction(
      runtime,
      jsi::PropNameID::forAscii(runtime, "object"),
      1,
      [](jsi::Runtime& rt, const jsi::Value& thisVal,
         const jsi::Value* args, size_t count) -> jsi::Value {
        // 这里从原生存储中读取并返回
        // return jsi::String::createFromUtf8(rt, ...);
        return jsi::Value::undefined();
      }
    );
  }

  return jsi::Value::undefined();
}

那么在JS 层就可以这样使用:

js 复制代码
nativeStorage.setObject('greeting', 'Hello JSI!');
const text = nativeStorage.object('greeting');

C++ 调用JS

调用 JS 函数,大概可以分两种情况,分别是回调函数和Promise 交互

回调函数
cpp 复制代码
void registerClickHandler(Runtime& runtime) {
  // 获取 JS 回调
  Value onClickValue = runtime.global().getProperty(runtime, "onClick");

  if (!onClickValue.isObject()) {
    throw JSError(runtime, "onClick is not defined");
  }

  Object onClickObj = onClickValue.getObject(runtime);
  if (!onClickObj.isFunction(runtime)) {
    throw JSError(runtime, "onClick is not a function");
  }

  Function onClick = onClickObj.getFunction(runtime);

  // C++ 事件触发时调用
  // 必须在 JS 线程执行
  onClick.call(runtime, 
      String::createFromAscii(runtime, "button1"),
      Value(100),  // x
      Value(200)); // y
}
Promise 交互
cpp 复制代码
Value createPromise(Runtime& runtime) {
  // 获取 Promise 构造函数
  Object promiseConstructor = runtime.global()
      .getPropertyAsObject(runtime, "Promise");

  // 创建 executor 函数
  auto executor = [](Runtime& rt, const Value&, const Value* args, size_t count) {
    Function resolve = args[0].getObject(rt).getFunction(rt);
    Function reject = args[1].getObject(rt).getFunction(rt);

    // 模拟异步操作
    std::thread([&rt, resolve = std::move(resolve)]() mutable {
      std::this_thread::sleep_for(std::chrono::seconds(1));

      // 实际需要 MessageQueue 调度
      resolve.call(rt, Value(42));
    }).detach();

    return Value::undefined();
  };

  Function executorFunc = Function::createFromHostFunction(
      runtime,
      PropNameID::forAscii(runtime, "executor"),
      2,
      executor);

  // 调用 new Promise(executor)
  return promiseConstructor.asFunction(runtime)
      .callAsConstructor(runtime, executorFunc);
}
访问 JS 对象属性
cpp 复制代码
void opObject(Runtime& runtime, const Object& obj) {
  // 1. 获取所有属性名
  Array propNames = obj.getPropertyNames(runtime);
  size_t length = propNames.size(runtime);

  std::cout << "Object properties:" << std::endl;
  for (size_t i = 0; i < length; i++) {
    Value nameValue = propNames.getValueAtIndex(runtime, i);
    if (nameValue.isString()) {
      std::string name = nameValue.getString(runtime).utf8(runtime);

      // 2. 获取属性值
      Value propValue = obj.getProperty(runtime, name.c_str());

      // 3. 类型判断
      std::string type;
      if (propValue.isUndefined()) type = "undefined";
      else if (propValue.isNull()) type = "null";
      else if (propValue.isBool()) type = "boolean";
      else if (propValue.isNumber()) type = "number";
      else if (propValue.isString()) type = "string";
      else if (propValue.isObject()) type = "object";

      std::cout << "  " << name << ": " << type << std::endl;
    }
  }
}

线程安全

由于JSI并不是线程安全的,如果直接在子线程调用JSI的相关接口,会导致闪退。因此,在使用JSI时,线程安全问题十分重要,必须谨慎。

  • jsi::Runtime 的实现(Hermes/JSC/V8)其内部状态、GC、对象分配都不是线程安全的
  • 必须保证所有 JSI 操作在单一 JS 线程上串行执行
  • 跨线程访问会导致数据竞争、内存损坏、崩溃

我们来看一下JSI提供的解决方案,源码react-native/packages/react-native/ReactCommon/callinvoker/ReactCommon/CallInvoker.h

cpp 复制代码
class CallInvoker {
 public:
  // 异步调度到 JS 线程(最常用)
  virtual void invokeAsync(std::function<void(jsi::Runtime&)>&& func) noexcept = 0;
  
  // 同步调用(阻塞当前线程直到 JS 线程执行完成)
  virtual void invokeSync(std::function<void(jsi::Runtime&)>&& func) = 0;
  
  virtual ~CallInvoker() = default;
};

也就是说,我们必须通过invokeAsync把执行相关JSI操作的闭包发送到JS线程执行。这里的invokeAsync可以从任意线程安全调用,不阻塞JS线程。注意,JS线程的阻塞,会直接导致UI的卡顿。

接下来,我们看一个结合Promise的JSI异步线程处理的完整示例:

cpp 复制代码
#include <jsi/jsi.h>
#include <ReactCommon/CallInvoker.h>
#include <thread>
#include <chrono>

using namespace facebook::jsi;

class NetworkModule : public jsi::HostObject {
 private:
  std::shared_ptr<CallInvoker> jsInvoker_;

 public:
  NetworkModule(std::shared_ptr<CallInvoker> jsInvoker) 
      : jsInvoker_(std::move(jsInvoker)) {}

  Value get(Runtime& runtime, const PropNameID& name) override {
    auto methodName = name.utf8(runtime);
  
    if (methodName == "fetchAsync") {
      return unction::createFromHostFunction(
          runtime,
          name,
          1,
          [this](Runtime& rt, const Value&, const Value* args, size_t count) -> Value {
            std::string url = args[0].getString(rt).utf8(rt);
        
            // 1. 获取 Promise 构造函数
            Object promiseConstructor = rt.global().getPropertyAsObject(rt, "Promise");
        
            // 2. 创建 executor 函数
            auto executor = Function::createFromHostFunction(
                rt,
                PropNameID::forAscii(rt, "executor"),
                2,
                [this, url](Runtime& runtime, const Value&, const Value* args, size_t) -> Value {
                  // 3. 保存 resolve/reject(使用 shared_ptr 延长生命周期)
                  auto resolve = std::make_shared<Function>(
                      args[0].getObject(rt).getFunction(rt));
                  auto reject = std::make_shared<Function>(
                      args[1].getObject(rt).getFunction(rt));
              
                  // 4. 后台线程执行
                  std::thread([this, resolve, reject, url]() {
                    try {
                      // 模拟网络请求
                      std::this_thread::sleep_for(std::chrono::seconds(1));
                      std::string result = "Response from " + url;
                  
                      // 5. 调度到 JS 线程
                      jsInvoker_->invokeAsync([resolve, result](Runtime& rt) {
                        // createFromUtf8 这类JSI API必须在JS线程执行
                        resolve->call(rt, String::createFromUtf8(rt, result));
                      });
                    } catch (const std::exception& e) {
                      jsInvoker_->invokeAsync([reject, msg = std::string(e.what())](Runtime& rt) {
                        reject->call(rt, String::createFromUtf8(rt, msg));
                      });
                    }
                  }).detach();
              
                  return Value::undefined();
                });
        
            // 6. 返回 new Promise(executor)
            return promiseConstructor.asFunction(rt).callAsConstructor(rt, executor);
          });
    }
  
    return Value::undefined();
  }
};

这样,上层JS 调用fetchAsync方法时,就会得到一个Promise对象,直到底层的子线程执行完任务后,将结果返回,上层Promise才会返回结果。整个耗时操作都由底层C++线程完成,不会阻塞JS线程,在整个耗时任务期间,JS线程都可以继续执行其他任务。

一些工具类

关于Promise 辅助类的使用。可以查看头文件react-native/packages/react-native/ReactCommon/react/nativemodule/core/ReactCommon/TurboModuleUtils.h

cpp 复制代码
namespace facebook::react {

struct Promise : public LongLivedObject {
  Promise(jsi::Runtime &rt, jsi::Function resolve, jsi::Function reject);

  void resolve(const jsi::Value &result);
  void reject(const std::string &message);

  jsi::Function resolve_;
  jsi::Function reject_;
};

using PromiseSetupFunctionType = std::function<void(jsi::Runtime &rt, std::shared_ptr<Promise>)>;
jsi::Value createPromiseAsJSIValue(jsi::Runtime &rt, PromiseSetupFunctionType &&func);

} 

使用示例:

cpp 复制代码
// 在 HostFunction 中返回 Promise
return createPromiseAsJSIValue(rt, [jsInvoker](Runtime& rt, std::shared_ptr<Promise> promise) {
  // 异步操作
  std::thread([jsInvoker, promise]() {
    // 后台工作...
  
    // 完成后调度到 JS 线程
    jsInvoker->invokeAsync([promise](Runtime& rt) {
      promise->resolve(Value(42));  // 或 promise->reject("error")
    });
  }).detach();
});

LongLivedObject 可以防止过早的内存回收:

cpp 复制代码
class MyData : public LongLivedObject {
 public:
  MyData(Runtime& rt) : LongLivedObject(rt) {}
  
  void done() {
    allowRelease();  // 允许被回收
  }
};

// 使用
auto data = std::make_shared<MyData>(runtime);
LongLivedObjectCollection::get(runtime).add(data);  // 防止回收
// ... 使用 data
data->allowRelease();  // 不需要时记得释放

常见错误示例

错误 1:直接在后台线程调用 JSI
cpp 复制代码
// ❌ 错误
std::thread([&runtime, callback]() {
  callback.call(runtime, Value(42));  // CRASH!
}).detach();

// ✅ 正确
std::thread([jsInvoker, callback = std::move(callback)]() mutable {
  jsInvoker->invokeAsync([callback = std::move(callback)](Runtime& rt) {
    callback.call(rt, Value(42));
  });
}).detach();
错误 2:在异步回调中直接使用 runtime 引用
cpp 复制代码
// ❌ 错误:runtime 引用可能失效
void asyncOp(Runtime& runtime, Function callback) {
  std::thread([&runtime, callback]() {  // 引用捕获危险
    jsInvoker->invokeAsync([&runtime, callback](Runtime&) {
      callback.call(runtime, Value(42));  // runtime 可能已销毁
    });
  }).detach();
}

// ✅ 正确:使用 lambda 传入的 runtime
void asyncOp(Runtime& runtime, Function callback, std::shared_ptr<CallInvoker> jsInvoker) {
  jsInvoker->invokeAsync([callback = std::move(callback)](Runtime& rt) {
    callback.call(rt, Value(42));  // 使用 lambda 参数rt
  });
}
错误 3:忘记 Promise 生命周期管理
cpp 复制代码
// ❌ 错误:Promise 可能被释放
auto promise = std::make_shared<Promise>(rt, resolve, reject);
std::thread([promise]() {
  // Promise 可能被释放
}).detach();

// ✅ 正确:使用 createPromiseAsJSIValue(自动管理)
return createPromiseAsJSIValue(rt, [](Runtime& rt, std::shared_ptr<Promise> promise) {
  // promise 已自动加入 LongLivedObjectCollection
});

关于createPromiseAsJSIValue函数,前面已经演示过了。

错误处理

看一个完整JSI错误处理示例:

cpp 复制代码
Value safeCall(Runtime& runtime, const Function& func, const Value* args, size_t count) {
  try {
    return func.call(runtime, args, count);
  } catch (const JSError& e) {
    // JS 异常
    std::cerr << "JS Error: " << e.getMessage() << std::endl;
    std::cerr << "Stack: " << e.getStack() << std::endl;
    throw;
  } catch (const JSINativeException& e) {
    // JSI 原生异常
    std::cerr << "Native Error: " << e.what() << std::endl;
    throw;
  } catch (const std::exception& e) {
    // 其他 C++ 异常
    std::cerr << "C++ Error: " << e.what() << std::endl;
    throw JSError(runtime, e.what());
  }
}

追踪详细的异常栈:

cpp 复制代码
void executeWithStackTrace(Runtime& runtime, const std::string& code) {
  try {
    runtime.evaluateJavaScript(
        std::make_shared<StringBuffer>(code),
        "debug.js");
  } catch (const JSError& e) {
    std::cerr << "=== JavaScript Error ===" << std::endl;
    std::cerr << "Message: " << e.getMessage() << std::endl;
    std::cerr << "Stack:\n" << e.getStack() << std::endl;
  
    // 可以进一步解析堆栈
    std::istringstream stream(e.getStack());
    std::string line;
    int frameNum = 0;
    while (std::getline(stream, line)) {
      std::cerr << "  #" << frameNum++ << " " << line << std::endl;
    }
  }
}
相关推荐
开始学java2 小时前
别再写“一锅端”的 useEffect!聊聊 React 副作用的逻辑分离
前端
百度地图汽车版2 小时前
【智图译站】基于异步时空图卷积网络的不规则交通预测
前端·后端
qq_12498707532 小时前
基于Spring Boot的“味蕾探索”线上零食购物平台的设计与实现(源码+论文+部署+安装)
java·前端·数据库·spring boot·后端·小程序
编程之路从0到12 小时前
React Native 之Android端 Bolts库
android·前端·react native
小酒星小杜2 小时前
在AI时代,技术人应该每天都要花两小时来构建一个自身的构建系统 - Build 篇
前端·vue.js·架构
lili-felicity2 小时前
React Native 鸿蒙跨平台开发:Animated 实现鸿蒙端组件的旋转 + 缩放组合动画
react native·react.js·harmonyos
奔跑的web.2 小时前
TypeScript 全面详解:对象类型的语法规则
开发语言·前端·javascript·typescript·vue
江上月5132 小时前
JMeter中级指南:从数据提取到断言校验全流程掌握
java·前端·数据库
代码猎人2 小时前
forEach和map方法有哪些区别
前端