react-native-nitro-modules 0.16.2 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,9 +6,6 @@
6
6
 
7
7
  // Forward declare a few of the common types that might have cyclic includes.
8
8
  namespace margelo::nitro {
9
- class Dispatcher;
10
-
11
- class JSPromise;
12
9
 
13
10
  template <typename T, typename Enable>
14
11
  struct JSIConverter;
@@ -16,11 +13,8 @@ struct JSIConverter;
16
13
 
17
14
  #include "JSIConverter.hpp"
18
15
 
19
- #include "Dispatcher.hpp"
20
- #include "JSPromise.hpp"
21
- #include "ThreadPool.hpp"
22
- #include "TypeInfo.hpp"
23
- #include <future>
16
+ #include "Promise.hpp"
17
+ #include <exception>
24
18
  #include <jsi/jsi.h>
25
19
  #include <memory>
26
20
 
@@ -28,66 +22,86 @@ namespace margelo::nitro {
28
22
 
29
23
  using namespace facebook;
30
24
 
31
- // std::future<T> <> Promise<T>
25
+ // Promise<T, std::exception> <> Promise<T>
32
26
  template <typename TResult>
33
- struct JSIConverter<std::future<TResult>> final {
34
- static inline std::future<TResult> fromJSI(jsi::Runtime&, const jsi::Value&) {
35
- throw std::runtime_error("Promise cannot be converted to a native type - it needs to be awaited first!");
27
+ struct JSIConverter<std::shared_ptr<Promise<TResult>>> final {
28
+ static inline std::shared_ptr<Promise<TResult>> fromJSI(jsi::Runtime& runtime, const jsi::Value& value) {
29
+ // Create new Promise and prepare onResolved / onRejected callbacks
30
+ auto promise = Promise<TResult>::create();
31
+ auto thenCallback = [&]() {
32
+ if constexpr (std::is_void_v<TResult>) {
33
+ // void: resolve()
34
+ return JSIConverter<std::function<void(std::monostate)>>::toJSI(runtime, [=](std::monostate) { promise->resolve(); });
35
+ } else {
36
+ // T: resolve(T)
37
+ return JSIConverter<std::function<void(TResult)>>::toJSI(runtime, [=](const TResult& result) { promise->resolve(result); });
38
+ }
39
+ }();
40
+ auto catchCallback = JSIConverter<std::function<void(std::exception_ptr)>>::toJSI(
41
+ runtime, [=](const std::exception_ptr& exception) { promise->reject(exception); });
42
+
43
+ // Chain .then listeners on JS Promise (onResolved and onRejected)
44
+ jsi::Object jsPromise = value.asObject(runtime);
45
+ jsi::Function thenFn = jsPromise.getPropertyAsFunction(runtime, "then");
46
+ thenFn.callWithThis(runtime, jsPromise, thenCallback, catchCallback);
47
+
48
+ return promise;
36
49
  }
37
50
 
38
- static inline jsi::Value toJSI(jsi::Runtime& runtime, std::future<TResult>&& arg) {
39
- auto sharedFuture = std::make_shared<std::future<TResult>>(std::move(arg));
40
- std::shared_ptr<Dispatcher> strongDispatcher = Dispatcher::getRuntimeGlobalDispatcher(runtime);
41
- std::weak_ptr<Dispatcher> weakDispatcher = strongDispatcher;
42
-
43
- return JSPromise::createPromise(runtime, [sharedFuture, weakDispatcher](jsi::Runtime& runtime, std::shared_ptr<JSPromise> promise) {
44
- // Spawn new async thread to synchronously wait for the `future<T>` to complete
45
- std::shared_ptr<ThreadPool> pool = ThreadPool::getSharedPool();
46
- pool->run([promise, &runtime, weakDispatcher, sharedFuture]() {
47
- // synchronously wait until the `future<T>` completes. we are running on a background task here.
48
- sharedFuture->wait();
49
-
50
- // the async function completed successfully, get a JS Dispatcher so we can resolve on JS Thread
51
- std::shared_ptr<Dispatcher> dispatcher = weakDispatcher.lock();
52
- if (!dispatcher) {
53
- Logger::log(LogLevel::Error, "JSIConverter",
54
- "Tried resolving Promise on JS Thread, but the `Dispatcher` has already been destroyed!");
55
- return;
51
+ static inline jsi::Value toJSI(jsi::Runtime& runtime, const std::shared_ptr<Promise<TResult>>& promise) {
52
+ if (promise->isPending()) {
53
+ // Get Promise ctor from global
54
+ jsi::Function promiseCtor = runtime.global().getPropertyAsFunction(runtime, "Promise");
55
+ jsi::HostFunctionType executor = [promise](jsi::Runtime& runtime, const jsi::Value& thisValue, const jsi::Value* arguments,
56
+ size_t count) -> jsi::Value {
57
+ // Add resolver listener
58
+ if constexpr (std::is_void_v<TResult>) {
59
+ // It's resolving to void.
60
+ auto resolver = JSIConverter<std::function<void()>>::fromJSI(runtime, arguments[0]);
61
+ promise->addOnResolvedListener(std::move(resolver));
62
+ } else {
63
+ // It's a type.
64
+ auto resolver = JSIConverter<std::function<void(TResult)>>::fromJSI(runtime, arguments[0]);
65
+ promise->addOnResolvedListener(std::move(resolver));
56
66
  }
57
-
58
- dispatcher->runAsync([&runtime, promise, sharedFuture]() mutable {
59
- try {
60
- if constexpr (std::is_void_v<TResult>) {
61
- // it's returning void, just return undefined to JS
62
- sharedFuture->get();
63
- promise->resolve(runtime, jsi::Value::undefined());
64
- } else {
65
- // it's returning a custom type, convert it to a jsi::Value
66
- TResult result = sharedFuture->get();
67
- jsi::Value jsResult = JSIConverter<TResult>::toJSI(runtime, std::forward<TResult>(result));
68
- promise->resolve(runtime, std::move(jsResult));
69
- }
70
- } catch (const std::exception& exception) {
71
- // the async function threw an error, reject the promise on JS Thread
72
- std::string what = exception.what();
73
- promise->reject(runtime, what);
74
- } catch (...) {
75
- // the async function threw a non-std error, try getting it
76
- std::string name = TypeInfo::getCurrentExceptionName();
77
- promise->reject(runtime, "Unknown non-std exception: " + name);
78
- }
79
-
80
- // This lambda owns the promise shared pointer, and we need to call its
81
- // destructor on the correct thread here - otherwise it might be called
82
- // from the waiterThread.
83
- promise = nullptr;
84
- });
85
- });
86
- });
67
+ // Add rejecter listener
68
+ auto rejecter = JSIConverter<std::function<void(std::exception_ptr)>>::fromJSI(runtime, arguments[1]);
69
+ promise->addOnRejectedListener(std::move(rejecter));
70
+
71
+ return jsi::Value::undefined();
72
+ };
73
+ // Call `Promise` constructor (aka create promise), and pass `executor` function
74
+ return promiseCtor.callAsConstructor(
75
+ runtime, jsi::Function::createFromHostFunction(runtime, jsi::PropNameID::forUtf8(runtime, "executor"), 2, executor));
76
+ } else if (promise->isResolved()) {
77
+ // Promise is already resolved - just return immediately
78
+ jsi::Object promiseObject = runtime.global().getPropertyAsObject(runtime, "Promise");
79
+ jsi::Function createResolvedPromise = promiseObject.getPropertyAsFunction(runtime, "resolve");
80
+ if constexpr (std::is_void_v<TResult>) {
81
+ // It's resolving to void.
82
+ return createResolvedPromise.call(runtime);
83
+ } else {
84
+ // It's resolving to a type.
85
+ jsi::Value result = JSIConverter<TResult>::toJSI(runtime, promise->getResult());
86
+ return createResolvedPromise.call(runtime, std::move(result));
87
+ }
88
+ } else if (promise->isRejected()) {
89
+ // Promise is already rejected - just return immediately
90
+ jsi::Object promiseObject = runtime.global().getPropertyAsObject(runtime, "Promise");
91
+ jsi::Function createRejectedPromise = promiseObject.getPropertyAsFunction(runtime, "reject");
92
+ jsi::Value error = JSIConverter<std::exception_ptr>::toJSI(runtime, promise->getError());
93
+ return createRejectedPromise.call(runtime, std::move(error));
94
+ } else {
95
+ throw std::runtime_error("Promise has invalid state!");
96
+ }
87
97
  }
88
98
 
89
- static inline bool canConvert(jsi::Runtime&, const jsi::Value&) {
90
- throw std::runtime_error("jsi::Value of type Promise cannot be converted to std::future yet!");
99
+ static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
100
+ if (!value.isObject()) {
101
+ return false;
102
+ }
103
+ jsi::Object object = value.getObject(runtime);
104
+ return object.hasProperty(runtime, "then");
91
105
  }
92
106
  };
93
107
 
@@ -183,7 +183,9 @@ struct JSIConverter<std::string> final {
183
183
 
184
184
  #include "JSIConverter+AnyMap.hpp"
185
185
  #include "JSIConverter+ArrayBuffer.hpp"
186
+ #include "JSIConverter+Exception.hpp"
186
187
  #include "JSIConverter+Function.hpp"
188
+ #include "JSIConverter+Future.hpp"
187
189
  #include "JSIConverter+HostObject.hpp"
188
190
  #include "JSIConverter+HybridObject.hpp"
189
191
  #include "JSIConverter+Optional.hpp"
@@ -5,6 +5,8 @@
5
5
  // Created by Marc Rousavy on 14.07.24.
6
6
  //
7
7
 
8
+ #pragma once
9
+
8
10
  #include <string>
9
11
 
10
12
  namespace margelo::nitro {
@@ -8,6 +8,7 @@
8
8
  #include "HybridObjectPrototype.hpp"
9
9
  #include "NitroDefines.hpp"
10
10
  #include "NitroLogger.hpp"
11
+ #include "TypeInfo.hpp"
11
12
 
12
13
  namespace margelo::nitro {
13
14
 
@@ -7,7 +7,6 @@
7
7
 
8
8
  #pragma once
9
9
 
10
- #include <jsi/jsi.h>
11
10
  #include <type_traits>
12
11
 
13
12
  namespace margelo::nitro {
@@ -11,38 +11,58 @@
11
11
 
12
12
  namespace margelo::nitro {
13
13
 
14
- ThreadPool::ThreadPool(const char* name, size_t numThreads) : _isAlive(true), _name(name) {
15
- Logger::log(LogLevel::Info, TAG, "Creating ThreadPool \"%s\" with %i threads...", name, numThreads);
14
+ ThreadPool::ThreadPool(const char* name, size_t initialThreadsCount, size_t maxThreadsCount)
15
+ : _isAlive(true), _threadCountLimit(maxThreadsCount), _name(name) {
16
+ Logger::log(LogLevel::Info, TAG, "Creating ThreadPool \"%s\" with %i initial threads (max: %i)...", name, initialThreadsCount,
17
+ maxThreadsCount);
16
18
 
17
- for (size_t i = 0; i < numThreads; ++i) {
18
- std::string threadName = std::string(name) + "-" + std::to_string(i + 1);
19
- _workers.emplace_back([this, threadName] {
20
- // Set the Thread's name
21
- ThreadUtils::setThreadName(threadName);
19
+ for (size_t i = 0; i < initialThreadsCount; i++) {
20
+ addThread();
21
+ }
22
+ }
23
+
24
+ void ThreadPool::addThread() {
25
+ std::unique_lock<std::mutex> lock(_queueMutex);
26
+ if (!_isAlive) {
27
+ return;
28
+ }
29
+
30
+ size_t i = ++_threadCount;
31
+ Logger::log(LogLevel::Info, TAG, "Adding Thread %i to ThreadPool \"%s\"...", i, _name);
32
+
33
+ std::string threadName = std::string(_name) + "-" + std::to_string(i);
34
+ _workers.emplace_back([this, threadName] {
35
+ // Set the Thread's name
36
+ ThreadUtils::setThreadName(threadName);
22
37
 
23
- // Start the run-loop
24
- while (true) {
25
- std::function<void()> task;
26
- {
27
- // Lock on the mutex so only one Worker receives the condition signal at a time
28
- std::unique_lock<std::mutex> lock(_queueMutex);
29
- this->_condition.wait(lock, [this] { return !_isAlive || !_tasks.empty(); });
30
- if (!_isAlive && _tasks.empty()) {
31
- // ThreadPool is dead - stop run-loop.
32
- return;
33
- }
34
- // Schedule the oldest task
35
- task = std::move(_tasks.front());
36
- _tasks.pop();
38
+ // Start the run-loop
39
+ while (true) {
40
+ std::function<void()> task;
41
+ {
42
+ // Lock on the mutex so only one Worker receives the condition signal at a time
43
+ std::unique_lock<std::mutex> lock(_queueMutex);
44
+ this->_condition.wait(lock, [this] { return !_isAlive || !_tasks.empty(); });
45
+ if (!_isAlive && _tasks.empty()) {
46
+ // ThreadPool is dead - stop run-loop.
47
+ return;
37
48
  }
38
- // Run it (outside of the mutex so others can run in parallel)
39
- task();
49
+ // Schedule the oldest task
50
+ task = std::move(_tasks.front());
51
+ _tasks.pop();
40
52
  }
41
- });
42
- }
53
+ // Run it (outside of the mutex so others can run in parallel)
54
+ task();
55
+ }
56
+ });
57
+ _condition.notify_all();
43
58
  }
44
59
 
45
60
  void ThreadPool::run(std::function<void()>&& task) {
61
+ // If there are tasks still waiting to be finished, just start a new Thread.
62
+ if (!_tasks.empty() && _threadCount < _threadCountLimit) {
63
+ addThread();
64
+ }
65
+ // New scope because of RAII lock
46
66
  {
47
67
  // lock on the mutex - we want to emplace the task back in the queue
48
68
  std::unique_lock<std::mutex> lock(_queueMutex);
@@ -71,13 +91,8 @@ ThreadPool::~ThreadPool() {
71
91
  }
72
92
  }
73
93
 
74
- std::shared_ptr<ThreadPool> ThreadPool::getSharedPool() {
75
- static std::shared_ptr<ThreadPool> shared;
76
- if (shared == nullptr) {
77
- int availableThreads = std::thread::hardware_concurrency();
78
- auto numThreads = std::min(availableThreads, 3);
79
- shared = std::make_shared<ThreadPool>("nitro-thread", numThreads);
80
- }
94
+ ThreadPool& ThreadPool::shared() {
95
+ static ThreadPool shared("nitro-thread", 3, 10);
81
96
  return shared;
82
97
  }
83
98
 
@@ -22,10 +22,13 @@ namespace margelo::nitro {
22
22
  class ThreadPool final {
23
23
  public:
24
24
  /**
25
- * Create a new ThreadPool with the given number of fixed workers/threads.
25
+ * Create a new ThreadPool with the given number of minimum workers/threads.
26
+ * The Thread Pool can expand on the fly if it is busy.
26
27
  */
27
- explicit ThreadPool(const char* const name, size_t numThreads);
28
+ explicit ThreadPool(const char* const name, size_t initialThreadsCount, size_t maxThreadsCount);
28
29
  ~ThreadPool();
30
+ ThreadPool(const ThreadPool&) = delete;
31
+ ThreadPool(ThreadPool&&) = delete;
29
32
 
30
33
  /**
31
34
  * Schedules the given task asynchronously on the ThreadPool.
@@ -33,12 +36,19 @@ public:
33
36
  */
34
37
  void run(std::function<void()>&& task);
35
38
 
39
+ private:
40
+ /**
41
+ * Adds a new Thread to the current Thread Pool.
42
+ * This grows the size by one, and potentially starts work sooner if other Threads are busy.
43
+ */
44
+ void addThread();
45
+
36
46
  public:
37
47
  /**
38
48
  * Get a static singleton instance - a shared ThreadPool.
39
49
  * The shared ThreadPool has 3 threads.
40
50
  */
41
- static std::shared_ptr<ThreadPool> getSharedPool();
51
+ static ThreadPool& shared();
42
52
 
43
53
  private:
44
54
  std::vector<std::thread> _workers;
@@ -46,6 +56,8 @@ private:
46
56
  std::mutex _queueMutex;
47
57
  std::condition_variable _condition;
48
58
  std::atomic<bool> _isAlive;
59
+ std::atomic<size_t> _threadCount;
60
+ size_t _threadCountLimit;
49
61
  const char* _name;
50
62
  static constexpr auto TAG = "ThreadPool";
51
63
  };
@@ -0,0 +1,34 @@
1
+ //
2
+ // AssertPromiseState.hpp
3
+ // NitroModules
4
+ //
5
+ // Created by Marc Rousavy on 20.11.24.
6
+ //
7
+
8
+ #pragma once
9
+
10
+ namespace margelo::nitro {
11
+ template <typename TResult>
12
+ class Promise;
13
+ } // namespace margelo::nitro
14
+
15
+ #include "Promise.hpp"
16
+ #include "TypeInfo.hpp"
17
+ #include <exception>
18
+ #include <string>
19
+
20
+ namespace margelo::nitro {
21
+
22
+ enum PromiseTask { WANTS_TO_RESOLVE, WANTS_TO_REJECT };
23
+
24
+ template <typename TResult>
25
+ void assertPromiseState(Promise<TResult>& promise, PromiseTask task) {
26
+ if (!promise.isPending()) [[unlikely]] {
27
+ std::string taskString = task == WANTS_TO_RESOLVE ? "resolve" : "reject";
28
+ std::string state = promise.isResolved() ? "resolved" : "rejected";
29
+ throw std::runtime_error("Cannot " + taskString + " Promise<" + TypeInfo::getFriendlyTypename<TResult>() + "> - it is already " +
30
+ state + "!");
31
+ }
32
+ }
33
+
34
+ } // namespace margelo::nitro
@@ -111,7 +111,7 @@ private:
111
111
  bool* _isDeleted;
112
112
  std::atomic_size_t* _strongRefCount;
113
113
  std::atomic_size_t* _weakRefCount;
114
- std::mutex* _mutex;
114
+ std::recursive_mutex* _mutex;
115
115
  };
116
116
 
117
117
  } // namespace margelo::nitro
@@ -34,7 +34,7 @@ public:
34
34
 
35
35
  explicit OwningReference(T* value)
36
36
  : _value(value), _isDeleted(new bool(false)), _strongRefCount(new std::atomic_size_t(1)), _weakRefCount(new std::atomic_size_t(0)),
37
- _mutex(new std::mutex()) {}
37
+ _mutex(new std::recursive_mutex()) {}
38
38
 
39
39
  OwningReference(const OwningReference& ref)
40
40
  : _value(ref._value), _isDeleted(ref._isDeleted), _strongRefCount(ref._strongRefCount), _weakRefCount(ref._weakRefCount),
@@ -229,7 +229,7 @@ private:
229
229
  bool* _isDeleted;
230
230
  std::atomic_size_t* _strongRefCount;
231
231
  std::atomic_size_t* _weakRefCount;
232
- std::mutex* _mutex;
232
+ std::recursive_mutex* _mutex;
233
233
  };
234
234
 
235
235
  } // namespace margelo::nitro
@@ -24,17 +24,6 @@ struct TypeInfo final {
24
24
  public:
25
25
  TypeInfo() = delete;
26
26
 
27
- /**
28
- * Get the name of the currently thrown exception
29
- */
30
- static inline const char* getCurrentExceptionName() {
31
- #if __has_include(<cxxabi.h>)
32
- return __cxxabiv1::__cxa_current_exception_type()->name();
33
- #else
34
- return "<unknown>";
35
- #endif
36
- }
37
-
38
27
  static inline std::string replaceRegex(const std::string& original, const std::string& pattern, const std::string& replacement) {
39
28
  std::regex re(pattern);
40
29
  return std::regex_replace(original, re, replacement);
@@ -112,6 +101,18 @@ public:
112
101
  std::string string = stream.str();
113
102
  return string.substr(0, string.length() - 2);
114
103
  }
104
+
105
+ /**
106
+ * Get the name of the currently thrown exception
107
+ */
108
+ static inline std::string getCurrentExceptionName() {
109
+ #if __has_include(<cxxabi.h>)
110
+ std::string name = __cxxabiv1::__cxa_current_exception_type()->name();
111
+ return demangleName(name);
112
+ #else
113
+ return "<unknown>";
114
+ #endif
115
+ }
115
116
  };
116
117
 
117
118
  } // namespace margelo::nitro
@@ -12,6 +12,31 @@ import Foundation
12
12
  *
13
13
  * Throw this error in Nitro Modules to provide clear and concise error messages to JS.
14
14
  */
15
- public enum RuntimeError: Error {
15
+ @frozen
16
+ public enum RuntimeError: Error, CustomStringConvertible {
16
17
  case error(withMessage: String)
18
+
19
+ public var description: String {
20
+ switch self {
21
+ case .error(let message): return message
22
+ }
23
+ }
24
+
25
+ /**
26
+ * Creates a new `RuntimeError` from the given C++ `std::exception`.
27
+ */
28
+ public static func from(cppError: std.exception_ptr) -> RuntimeError {
29
+ let message = margelo.nitro.get_exception_message(cppError)
30
+ return .error(withMessage: String(message))
31
+ }
32
+ }
33
+
34
+ public extension Error {
35
+ /**
36
+ * Converts this `Error` to a C++ `std::exception`.
37
+ */
38
+ func toCpp() -> std.exception_ptr {
39
+ let message = String(describing: self)
40
+ return margelo.nitro.make_exception(std.string(message))
41
+ }
17
42
  }
@@ -57,7 +57,7 @@ RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(install) {
57
57
  // 5. Install Nitro
58
58
  nitro::install(*runtime, dispatcher);
59
59
  return nil;
60
- } catch (std::exception& error) {
60
+ } catch (const std::exception& error) {
61
61
  // ?. Any C++ error occurred (probably in nitro::install()?)
62
62
  return [NSString stringWithCString:error.what() encoding:kCFStringEncodingUTF8];
63
63
  } catch (...) {
@@ -0,0 +1,30 @@
1
+ //
2
+ // RuntimeError.hpp
3
+ // NitroModules
4
+ //
5
+ // Created by Marc Rousavy on 19.11.24.
6
+ //
7
+
8
+ #pragma once
9
+
10
+ #include <exception>
11
+ #include <string>
12
+
13
+ namespace margelo::nitro {
14
+
15
+ static inline std::exception_ptr make_exception(const std::string& message) {
16
+ return std::make_exception_ptr(std::runtime_error(message));
17
+ }
18
+
19
+ static inline std::string get_exception_message(const std::exception_ptr& exception) {
20
+ try {
21
+ std::rethrow_exception(exception);
22
+ } catch (const std::exception& error) {
23
+ return error.what();
24
+ } catch (...) {
25
+ std::string errorName = TypeInfo::getCurrentExceptionName();
26
+ return "Unknown " + errorName + " exception";
27
+ }
28
+ }
29
+
30
+ } // namespace margelo::nitro
@@ -55,7 +55,11 @@ public:
55
55
  /**
56
56
  * Gets the underlying `std::function`.
57
57
  */
58
- const std::function<void()>& getFunction() {
58
+ const std::function<void()>& getFunction() const {
59
+ return _function;
60
+ }
61
+
62
+ std::function<void()> getFunctionCopy() const {
59
63
  return _function;
60
64
  }
61
65
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-native-nitro-modules",
3
- "version": "0.16.2",
3
+ "version": "0.18.0",
4
4
  "description": "Insanely fast native C++, Swift or Kotlin modules with a statically compiled binding layer to JSI.",
5
5
  "main": "lib/commonjs/index",
6
6
  "module": "lib/module/index",
@@ -28,6 +28,8 @@
28
28
  "ios",
29
29
  "android",
30
30
  "visionOS",
31
+ "tvOS",
32
+ "macOS",
31
33
  "cpp",
32
34
  "framework",
33
35
  "react",
@@ -70,7 +72,7 @@
70
72
  "jest": "*",
71
73
  "react": "*",
72
74
  "react-native": "*",
73
- "react-native-builder-bob": "^0.31.0"
75
+ "react-native-builder-bob": "^0.33.1"
74
76
  },
75
77
  "peerDependencies": {
76
78
  "react": "*",
@@ -1,58 +0,0 @@
1
- #include "JSPromise.hpp"
2
- #include "JSICache.hpp"
3
- #include "NitroLogger.hpp"
4
- #include <jsi/jsi.h>
5
-
6
- namespace margelo::nitro {
7
-
8
- using namespace facebook;
9
-
10
- JSPromise::JSPromise(jsi::Runtime& runtime, jsi::Function&& resolver, jsi::Function&& rejecter) {
11
- auto functionCache = JSICache::getOrCreateCache(runtime);
12
- _resolver = functionCache.makeShared(std::move(resolver));
13
- _rejecter = functionCache.makeShared(std::move(rejecter));
14
- }
15
-
16
- jsi::Value JSPromise::createPromise(jsi::Runtime& runtime, RunPromise&& run) {
17
- // Get Promise ctor from global
18
- auto promiseCtor = runtime.global().getPropertyAsFunction(runtime, "Promise");
19
-
20
- auto promiseCallback = jsi::Function::createFromHostFunction(
21
- runtime, jsi::PropNameID::forUtf8(runtime, "PromiseCallback"), 2,
22
- [run = std::move(run)](jsi::Runtime& runtime, const jsi::Value& thisValue, const jsi::Value* arguments, size_t count) -> jsi::Value {
23
- // Get resolver and rejecter
24
- auto resolver = arguments[0].getObject(runtime).getFunction(runtime);
25
- auto rejecter = arguments[1].getObject(runtime).getFunction(runtime);
26
- // Create `Promise` type that wraps the JSI callbacks
27
- auto promise = std::make_shared<JSPromise>(runtime, std::move(resolver), std::move(rejecter));
28
- // Call `run` callback
29
- run(runtime, promise);
30
-
31
- return jsi::Value::undefined();
32
- });
33
-
34
- return promiseCtor.callAsConstructor(runtime, promiseCallback);
35
- }
36
-
37
- void JSPromise::resolve(jsi::Runtime& runtime, jsi::Value&& result) {
38
- OwningLock<jsi::Function> lock = _resolver.lock();
39
-
40
- if (!_resolver) {
41
- Logger::log(LogLevel::Error, TAG, "Promise resolver function has already been deleted! Ignoring call..");
42
- return;
43
- }
44
- _resolver->call(runtime, std::move(result));
45
- }
46
-
47
- void JSPromise::reject(jsi::Runtime& runtime, std::string message) {
48
- OwningLock<jsi::Function> lock = _rejecter.lock();
49
-
50
- if (!_rejecter) {
51
- Logger::log(LogLevel::Error, TAG, "Promise rejecter function has already been deleted! Ignoring call..");
52
- return;
53
- }
54
- jsi::JSError error(runtime, message);
55
- _rejecter->call(runtime, error.value());
56
- }
57
-
58
- } // namespace margelo::nitro