1

我需要在我的 React Native iOS 应用程序上使用来自 javascript 代码的自定义原生 fetch 函数,它必须是异步的,并且与标准函数一样工作。我想用 React Native JSI 来实现它,那么如何将异步函数暴露给 javascript?

4

1 回答 1

1

要将异步函数从本机公开到 javascript,您可以使用PromiseReact Native 运行时中的对象并使用其构造函数用您的代码构建 Promise。例如,“nativeFetch”函数的实现有NSURLSession

AppDelegate.mm

#include <jsi/jsi.h>
#import <React/RCTBridge+Private.h>

using namespace facebook::jsi;
using namespace std;

void installNativeFetch(Runtime& runtime) {
  auto nativeFetch = Function::createFromHostFunction(runtime,
                                                      PropNameID::forAscii(runtime, "nativeFetch"),
                                                      1,
                                                      [](Runtime &runtime, const Value &thisValue, const Value *args, size_t count) -> Value {
    const string strURL = args[0].asString(runtime).utf8(runtime);
    
    // Get global Promise
    auto promise = runtime.global().getPropertyAsFunction(runtime, "Promise");
    return promise.callAsConstructor(runtime, Function::createFromHostFunction(runtime,
                                                                               PropNameID::forAscii(runtime, "executor"),
                                                                               2,
                                                                               [strURL](Runtime &runtime, const Value &thisValue, const Value *args, size_t) -> Value {
      auto resolve = make_shared<Value>(runtime, args[0]);
      auto reject = make_shared<Value>(runtime, args[1]);
      
      NSURL* url = [NSURL URLWithString:[NSString stringWithUTF8String:strURL.c_str()]];
      NSURLSessionDataTask* task = [NSURLSession.sharedSession dataTaskWithURL:url completionHandler:^(NSData* data, NSURLResponse* response, NSError* error) {
        if (error != nil) {
          // Reject
          auto value = Value(runtime, String::createFromUtf8(runtime, error.localizedDescription.UTF8String));
          reject->asObject(runtime).asFunction(runtime).call(runtime, move(value));
        }
        else {
          auto result = Object(runtime);
          int statusCode = (int)((NSHTTPURLResponse*)response).statusCode;
          result.setProperty(runtime, "statusCode", statusCode);
          result.setProperty(runtime, "statusText", String::createFromUtf8(runtime, [NSHTTPURLResponse localizedStringForStatusCode:statusCode].UTF8String));
          
          // Resolve
          resolve->asObject(runtime).asFunction(runtime).call(runtime, move(result));
        }
      }];
      [task resume];
      return {};
    }));
  });
  
  // Bind our function with the javascript runtime global object
  runtime.global().setProperty(runtime, "nativeFetch", nativeFetch);
}

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

  ...

  // Runtime notification
  [NSNotificationCenter.defaultCenter addObserverForName:RCTJavaScriptDidLoadNotification object:nil queue:nil
                                              usingBlock:^(NSNotification* notification) {
    // Get runtime
    RCTCxxBridge* cxxbridge = (RCTCxxBridge*)notification.userInfo[@"bridge"];
    if (cxxbridge.runtime) {
      Runtime& runtime = *(Runtime*)cxxbridge.runtime;
      
      installNativeFetch(runtime);
    }
  }];
  
  return YES;
}

应用程序.js

async function get(url) {
    try {
        const result = await nativeFetch(url);
        console.log(result);
    }
    catch (e) {
        console.log("Error: " + e);
    }
}

...

get('https://unknown');
get('https://google.com');

输出:

{ statusCode: 200, statusText: 'no error' }
Error: A server with the specified hostname could not be found.

注意:由于示例使用 JSI 进行同步本地方法访问,远程调试(例如使用 Chrome)不再可能。相反,您应该使用 Flipper 来调试您的 JS 代码。

于 2022-01-27T14:22:05.210 回答