ammarahm-ed / react-native-jsi-template

Template library and blog that explain how JSI modules are built from scratch in React Native
https://blog.notesnook.com/getting-started-react-native-jsi/
MIT License
161 stars 24 forks source link

Feature: Include promise example #7

Open mfbx9da4 opened 2 years ago

mfbx9da4 commented 2 years ago

For my use case I would like the JSI function call to be asynchronous. I would still like to use the JSI for returning data since there will be a lot of data returned and I don't want to pay serialization costs, however I don't want to block the JS thread. It would be great if there was an example included in this repo which explained how to return a JSI promise.

ammarahm-ed commented 2 years ago

@mfbx9da4 I have seen it being used in some libraries. Will see how promises work.

mfbx9da4 commented 2 years ago

https://github.com/mrousavy/react-native-multithreading/blob/master/cpp/RNMultithreadingInstaller.cpp#L102 https://github.com/react-native-community/discussions-and-proposals/issues/91#issuecomment-892563719 https://github.com/BabylonJS/BabylonReactNative/blob/b3716d9dd7538ecf56ae670a311fba5b0d4b90b8/Modules/%40babylonjs/react-native/shared/BabylonNative.cpp#L201

looks like the way to do it is to invoke the JSRuntime's Promise

ammarahm-ed commented 2 years ago

Hey @mfbx9da4 since you have been working on callbacks and promises. When you get it to work on android and iOS, it would be great if you can send a PR with those functions so we can have them for future reference.

mfbx9da4 commented 2 years ago

Yeah would love to contribute when I get a moment. Incase somebody finds this issue and is looking for examples this is an example JSI function which returns a promise.

    auto promiseResolver = jsi::Function::createFromHostFunction(
        jsiRuntime,
        jsi::PropNameID::forAscii(jsiRuntime, "promiseResolver"),
        3,
        [cryptoPp, myqueue](jsi::Runtime& runtime, const jsi::Value& thisValue, const jsi::Value* arguments, size_t count) -> jsi::Value {

            CFTimeInterval startTime = CACurrentMediaTime();

            std::shared_ptr<jsi::Value> resolverValue;

            auto promiseConstructor = jsi::Function::createFromHostFunction(
                runtime,
                jsi::PropNameID::forAscii(runtime, "promiseConstructor"),
                2,
                [&resolverValue, cryptoPp, startTime, myqueue](jsi::Runtime& runtime, const jsi::Value& thisValue, const jsi::Value* arguments, size_t count) -> jsi::Value {
                    resolverValue = std::make_shared<jsi::Value>(arguments[0].asObject(runtime));
                    return jsi::Value::undefined();
                }
            );

            auto newPromise = runtime.global().getProperty(runtime, "Promise");
            auto promise = newPromise
                .asObject(runtime)
                .asFunction(runtime)
                .callAsConstructor(runtime, promiseConstructor);

            dispatch_async(myqueue, ^(void){
                jsi::Value decryptedJsValue = jsi::Value(runtime, jsi::String::createFromUtf8(runtime, "asdf"));
                resolverValue->asObject(runtime).asFunction(runtime).call(runtime, decryptedJsValue);
                NSLog(@"took in seconds %f", CACurrentMediaTime() - startTime);
            });

            return promise;
        }
    );
    jsiRuntime.global().setProperty(jsiRuntime, "promiseResolver", std::move(promiseResolver));

https://github.com/mfbx9da4/react-native-jsi-promise/blob/fa32a17488d7b7bacdbf58acd26a8b01623df480/ios/JsiPromise.mm#L39-L74

From some trial and error I found promises to sometimes be quite slow (~250ms), I found callbacks to be way faster. An example of how to implement callbacks can be found here.

mfbx9da4 commented 2 years ago

Hey Ammar,

An update on the JSI issue https://github.com/facebook/react-native/issues/33006

Thanks for your help!

On Thu, 27 Jan 2022 at 16:46, Ammar Ahmed @.***> wrote:

Hey @mfbx9da4 https://github.com/mfbx9da4 since you have been working on callbacks and promises. When you get it to work on android and iOS, it would be great if you can send a PR with those functions so we can have them for future reference.

— Reply to this email directly, view it on GitHub https://github.com/ammarahm-ed/react-native-jsi-template/issues/7#issuecomment-1023428138, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAM4YIYFLZ27DEKFJFSX2XTUYFZGLANCNFSM5MV3TJ5Q . You are receiving this because you were mentioned.Message ID: @.***>

ammarahm-ed commented 2 years ago

@mfbx9da4 The sporadic slowness might be explained in that sometimes the JS Queue would be busy with other tasks so the promise resolver has to wait a little in the queue for other tasks to complete. Once the queue is empty, the resolver would work again similar to how setTimeout/setInterval work. That's the best explanation I have. The reason is that because you are actually using a callback to get the value to JS which always takes 1-2ms and then wrapping it in a promise which sometimes takes upto 250ms.

nitish24p commented 1 year ago

Hey @mfbx9da4 In the callback example you mentioned above, did you get a crash when trying with hermes, i ended up getting a crash with the HermesExecutor.cpp, what i think has happened is the userCallback ref has probably got deallocated and that ends up crashing. Im very very new to Cxx and dont have much knowledge on either. It gets stuck in the reentranacy code check of HermesExecutor.cpp