返回

JSI小试牛刀——Native同步调用JS代码

前端

前言

上一篇有说到在有了JSI之后,JS和Native同时持有一个HostObject,那么JS和Native之间就有了同步调用的基础条件。实际上,在现在的RN(以0.59版本为例)中,已经实现了JS向Native代码的同步调用,在iOS中,可以通过宏RCT_EXPORT_BLOC…实现。

原理

Native同步调用JS代码的原理其实很简单,就是Native将要执行的任务封装成一个函数,然后将这个函数作为参数传递给JS,JS收到这个函数后,将其放入消息队列中,然后调用Native提供的回调函数来通知Native函数已经放入消息队列。Native收到回调后,将当前线程阻塞,等待JS执行完任务并返回结果。

实现

在iOS中,Native同步调用JS代码的实现需要用到以下几个宏:

  • RCT_EXPORT_BLOCKING_SYNCHRONOUSLY:此宏用于声明一个同步调用JS代码的Native方法。
  • RCT_EXPORT_METHOD:此宏用于声明一个Native方法。
  • resolve:此函数用于将结果返回给Native。
  • JSThread:此类用于获取当前的JS线程。
  • JSRUNLOOP_IS_CALLING_JS:此宏用于判断当前线程是否是JS线程。

下面是一个Native同步调用JS代码的示例:

@implementation NativeModule

RCT_EXPORT_BLOCKING_SYNCHRONOUSLY(NSString *, multiply:(nonnull NSNumber *)a withB:(nonnull NSNumber *)b) {
  // 获取JS线程
  JSThread *jsThread = [JSThread currentJSThread];
  
  // 创建一个函数并将其作为参数传递给JS
  __block NSString *result;
  RCTExecuteOnJSCallQueue(jsThread, ^{
    result = [self _multiply:a withB:b];
    
    // 调用回调函数通知Native函数已经放入消息队列
    resolve(result);
  });
  
  // 阻塞当前线程,等待JS执行完任务并返回结果
  while (JSRUNLOOP_IS_CALLING_JS()) {
    [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
  }
  
  return result;
}

- (NSString *)_multiply:(NSNumber *)a withB:(NSNumber *)b {
  return [NSString stringWithFormat:@"%d", [a intValue] * [b intValue]];
}

@end

在JS中,可以这样调用上面的Native方法:

const result = NativeModules.NativeModule.multiply(1, 2);

效率

Native同步调用JS代码的效率并不高,因为Native线程在等待JS执行任务期间是阻塞的。因此,不建议在Native代码中频繁使用同步调用JS代码。

性能

Native同步调用JS代码的性能与JS线程的执行效率密切相关。如果JS线程执行效率低,那么Native同步调用JS代码的性能也会很低。因此,在使用Native同步调用JS代码时,应确保JS线程的执行效率足够高。

线程

Native同步调用JS代码时,Native线程和JS线程是分开的。Native线程负责执行Native代码,JS线程负责执行JS代码。当Native线程调用JS代码时,Native线程会将要执行的任务封装成一个函数,然后将这个函数作为参数传递给JS线程。JS线程收到这个函数后,将其放入消息队列中,然后调用Native提供的回调函数来通知Native函数已经放入消息队列。Native收到回调后,将当前线程阻塞,等待JS线程执行完任务并返回结果。

消息队列

消息队列是JS线程用来处理任务的队列。当Native线程调用JS代码时,Native线程会将要执行的任务封装成一个函数,然后将这个函数作为参数传递给JS线程。JS线程收到这个函数后,将其放入消息队列中。然后,JS线程会依次从消息队列中取出任务并执行。

多线程

Native同步调用JS代码时,Native线程和JS线程是分开的。Native线程负责执行Native代码,JS线程负责执行JS代码。因此,Native同步调用JS代码时,可以实现多线程。

任务

Native同步调用JS代码时,Native线程会将要执行的任务封装成一个函数,然后将这个函数作为参数传递给JS线程。JS线程收到这个函数后,将其放入消息队列中。然后,JS线程会依次从消息队列中取出任务并执行。