react-native-nitro-modules 0.16.2 → 0.17.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.
@@ -0,0 +1,388 @@
1
+ //
2
+ // Created by Marc Rousavy on 18.11.24.
3
+ //
4
+
5
+ #pragma once
6
+
7
+ #include "ThreadPool.hpp"
8
+ #include "TypeInfo.hpp"
9
+ #include <exception>
10
+ #include <future>
11
+ #include <jsi/jsi.h>
12
+ #include <memory>
13
+ #include <mutex>
14
+ #include <variant>
15
+
16
+ namespace margelo::nitro {
17
+
18
+ using namespace facebook;
19
+
20
+ template <typename TResult, typename TError = std::exception>
21
+ class Promise final {
22
+ public:
23
+ using OnResolvedFunc = std::function<void(const TResult&)>;
24
+ using OnRejectedFunc = std::function<void(const TError&)>;
25
+
26
+ public:
27
+ // Promise cannot be deleted.
28
+ Promise(const Promise&) = delete;
29
+ // Promise can be moved.
30
+ Promise(Promise&&) = default;
31
+
32
+ private:
33
+ Promise() {
34
+ _mutex = std::make_unique<std::mutex>();
35
+ }
36
+
37
+ public:
38
+ /**
39
+ * Creates a new pending Promise that has to be resolved
40
+ * or rejected with `resolve(..)` or `reject(..)`.
41
+ */
42
+ static std::shared_ptr<Promise> create() {
43
+ return std::shared_ptr<Promise>(new Promise());
44
+ }
45
+
46
+ /**
47
+ * Creates a Promise that runs the given function `run` on a separate Thread pool.
48
+ */
49
+ static std::shared_ptr<Promise> async(std::function<TResult()>&& run) {
50
+ auto promise = create();
51
+ ThreadPool::shared().run([run = std::move(run), promise]() {
52
+ try {
53
+ // Run the code, then resolve.
54
+ TResult result = run();
55
+ promise->resolve(std::move(result));
56
+ } catch (const TError& exception) {
57
+ // It threw an std::exception.
58
+ promise->reject(exception);
59
+ } catch (...) {
60
+ // It threw a different error.
61
+ std::string name = TypeInfo::getCurrentExceptionName();
62
+ promise->reject(std::runtime_error("Unknown non-std error! Name: " + name));
63
+ }
64
+ });
65
+ return promise;
66
+ }
67
+
68
+ /**
69
+ * Creates a Promise and awaits the given future on a background Thread.
70
+ * Once the future resolves or rejects, the Promise resolves or rejects.
71
+ */
72
+ static std::shared_ptr<Promise> awaitFuture(std::future<TResult>&& future) {
73
+ auto sharedFuture = std::make_shared<std::future<TResult>>(std::move(future));
74
+ return async([sharedFuture = std::move(sharedFuture)]() { return sharedFuture->get(); });
75
+ }
76
+
77
+ /**
78
+ * Creates an immediately resolved Promise.
79
+ */
80
+ static std::shared_ptr<Promise> resolved(TResult&& result) {
81
+ auto promise = create();
82
+ promise->resolve(std::move(result));
83
+ return promise;
84
+ }
85
+ /**
86
+ * Creates an immediately rejected Promise.
87
+ */
88
+ static std::shared_ptr<Promise> rejected(TError&& error) {
89
+ auto promise = create();
90
+ promise->reject(std::move(error));
91
+ return promise;
92
+ }
93
+
94
+ public:
95
+ /**
96
+ * Resolves this Promise with the given result, and calls any pending listeners.
97
+ */
98
+ void resolve(TResult&& result) {
99
+ std::unique_lock lock(*_mutex);
100
+ _result = std::move(result);
101
+ for (const auto& onResolved : _onResolvedListeners) {
102
+ onResolved(std::get<TResult>(_result));
103
+ }
104
+ }
105
+ void resolve(const TResult& result) {
106
+ std::unique_lock lock(*_mutex);
107
+ _result = result;
108
+ for (const auto& onResolved : _onResolvedListeners) {
109
+ onResolved(std::get<TResult>(_result));
110
+ }
111
+ }
112
+ /**
113
+ * Rejects this Promise with the given error, and calls any pending listeners.
114
+ */
115
+ void reject(TError&& exception) {
116
+ std::unique_lock lock(*_mutex);
117
+ _result = std::move(exception);
118
+ for (const auto& onRejected : _onRejectedListeners) {
119
+ onRejected(std::get<TError>(_result));
120
+ }
121
+ }
122
+ void reject(const TError& exception) {
123
+ std::unique_lock lock(*_mutex);
124
+ _result = exception;
125
+ for (const auto& onRejected : _onRejectedListeners) {
126
+ onRejected(std::get<TError>(_result));
127
+ }
128
+ }
129
+
130
+ public:
131
+ /**
132
+ * Add a listener that will be called when the Promise gets resolved.
133
+ * If the Promise is already resolved, the listener will be immediately called.
134
+ */
135
+ void addOnResolvedListener(OnResolvedFunc&& onResolved) {
136
+ std::unique_lock lock(*_mutex);
137
+ if (std::holds_alternative<TResult>(_result)) {
138
+ // Promise is already resolved! Call the callback immediately
139
+ onResolved(std::get<TResult>(_result));
140
+ } else {
141
+ // Promise is not yet resolved, put the listener in our queue.
142
+ _onResolvedListeners.push_back(std::move(onResolved));
143
+ }
144
+ }
145
+ void addOnResolvedListener(const OnResolvedFunc& onResolved) {
146
+ std::unique_lock lock(*_mutex);
147
+ if (std::holds_alternative<TResult>(_result)) {
148
+ // Promise is already resolved! Call the callback immediately
149
+ onResolved(std::get<TResult>(_result));
150
+ } else {
151
+ // Promise is not yet resolved, put the listener in our queue.
152
+ _onResolvedListeners.push_back(onResolved);
153
+ }
154
+ }
155
+ void addOnResolvedListenerCopy(const std::function<void(TResult)>& onResolved) {
156
+ std::unique_lock lock(*_mutex);
157
+ if (std::holds_alternative<TResult>(_result)) {
158
+ // Promise is already resolved! Call the callback immediately
159
+ onResolved(std::get<TResult>(_result));
160
+ } else {
161
+ // Promise is not yet resolved, put the listener in our queue.
162
+ _onResolvedListeners.push_back(onResolved);
163
+ }
164
+ }
165
+
166
+ /**
167
+ * Add a listener that will be called when the Promise gets rejected.
168
+ * If the Promise is already rejected, the listener will be immediately called.
169
+ */
170
+ void addOnRejectedListener(OnRejectedFunc&& onRejected) {
171
+ std::unique_lock lock(*_mutex);
172
+ if (std::holds_alternative<TError>(_result)) {
173
+ // Promise is already rejected! Call the callback immediately
174
+ onRejected(std::get<TError>(_result));
175
+ } else {
176
+ // Promise is not yet rejected, put the listener in our queue.
177
+ _onRejectedListeners.push_back(std::move(onRejected));
178
+ }
179
+ }
180
+ void addOnRejectedListener(const OnRejectedFunc& onRejected) {
181
+ std::unique_lock lock(*_mutex);
182
+ if (std::holds_alternative<TError>(_result)) {
183
+ // Promise is already rejected! Call the callback immediately
184
+ onRejected(std::get<TError>(_result));
185
+ } else {
186
+ // Promise is not yet rejected, put the listener in our queue.
187
+ _onRejectedListeners.push_back(onRejected);
188
+ }
189
+ }
190
+
191
+ public:
192
+ /**
193
+ * Get the result of the Promise if it has been resolved.
194
+ * If the Promise is not resolved, this will throw.
195
+ */
196
+ inline const TResult& getResult() {
197
+ if (!isResolved()) {
198
+ throw std::runtime_error("Cannot get result when Promise is not yet resolved!");
199
+ }
200
+ return std::get<TResult>(_result);
201
+ }
202
+ /**
203
+ * Get the error of the Promise if it has been rejected.
204
+ * If the Promise is not rejected, this will throw.
205
+ */
206
+ inline const TError& getError() {
207
+ if (!isRejected()) {
208
+ throw std::runtime_error("Cannot get error when Promise is not yet rejected!");
209
+ }
210
+ return std::get<TError>(_result);
211
+ }
212
+
213
+ public:
214
+ /**
215
+ * Gets whether this Promise has been successfully resolved with a result, or not.
216
+ */
217
+ [[nodiscard]]
218
+ inline bool isResolved() const noexcept {
219
+ return std::holds_alternative<TResult>(_result);
220
+ }
221
+ /**
222
+ * Gets whether this Promise has been rejected with an error, or not.
223
+ */
224
+ [[nodiscard]]
225
+ inline bool isRejected() const noexcept {
226
+ return std::holds_alternative<TError>(_result);
227
+ }
228
+ /**
229
+ * Gets whether this Promise has not yet been resolved nor rejected.
230
+ */
231
+ [[nodiscard]]
232
+ inline bool isPending() const noexcept {
233
+ return std::holds_alternative<std::monostate>(_result);
234
+ }
235
+
236
+ private:
237
+ std::variant<std::monostate, TResult, TError> _result;
238
+ std::vector<OnResolvedFunc> _onResolvedListeners;
239
+ std::vector<OnRejectedFunc> _onRejectedListeners;
240
+ std::unique_ptr<std::mutex> _mutex;
241
+ };
242
+
243
+ // Specialization for void
244
+ template <typename TError>
245
+ class Promise<void, TError> final {
246
+ public:
247
+ using OnResolvedFunc = std::function<void()>;
248
+ using OnRejectedFunc = std::function<void(const TError&)>;
249
+
250
+ public:
251
+ Promise(const Promise&) = delete;
252
+ Promise(Promise&&) = default;
253
+
254
+ private:
255
+ Promise() {
256
+ _mutex = std::make_unique<std::mutex>();
257
+ }
258
+
259
+ public:
260
+ static std::shared_ptr<Promise> create() {
261
+ return std::shared_ptr<Promise>(new Promise());
262
+ }
263
+
264
+ static std::shared_ptr<Promise> async(std::function<void()>&& run) {
265
+ auto promise = create();
266
+ ThreadPool::shared().run([run = std::move(run), promise]() {
267
+ try {
268
+ // Run the code, then resolve.
269
+ run();
270
+ promise->resolve();
271
+ } catch (const TError& exception) {
272
+ // It threw an std::exception.
273
+ promise->reject(exception);
274
+ } catch (...) {
275
+ // It threw a different error.
276
+ std::string name = TypeInfo::getCurrentExceptionName();
277
+ promise->reject(std::runtime_error("Unknown non-std error! Name: " + name));
278
+ }
279
+ });
280
+ return promise;
281
+ }
282
+
283
+ static std::shared_ptr<Promise> awaitFuture(std::future<void>&& future) {
284
+ auto sharedFuture = std::make_shared<std::future<void>>(std::move(future));
285
+ return async([sharedFuture = std::move(sharedFuture)]() { sharedFuture->get(); });
286
+ }
287
+
288
+ static std::shared_ptr<Promise> resolved() {
289
+ auto promise = create();
290
+ promise->resolve();
291
+ return promise;
292
+ }
293
+ static std::shared_ptr<Promise> rejected(TError&& error) {
294
+ auto promise = create();
295
+ promise->reject(std::move(error));
296
+ return promise;
297
+ }
298
+
299
+ public:
300
+ void resolve() {
301
+ std::unique_lock lock(*_mutex);
302
+ _isResolved = true;
303
+ for (const auto& onResolved : _onResolvedListeners) {
304
+ onResolved();
305
+ }
306
+ }
307
+ void reject(TError&& exception) {
308
+ std::unique_lock lock(*_mutex);
309
+ _error = std::move(exception);
310
+ for (const auto& onRejected : _onRejectedListeners) {
311
+ onRejected(_error.value());
312
+ }
313
+ }
314
+ void reject(const TError& exception) {
315
+ std::unique_lock lock(*_mutex);
316
+ _error = exception;
317
+ for (const auto& onRejected : _onRejectedListeners) {
318
+ onRejected(_error.value());
319
+ }
320
+ }
321
+
322
+ public:
323
+ void addOnResolvedListener(OnResolvedFunc&& onResolved) {
324
+ std::unique_lock lock(*_mutex);
325
+ if (_isResolved) {
326
+ onResolved();
327
+ } else {
328
+ _onResolvedListeners.push_back(std::move(onResolved));
329
+ }
330
+ }
331
+ void addOnResolvedListener(const OnResolvedFunc& onResolved) {
332
+ std::unique_lock lock(*_mutex);
333
+ if (_isResolved) {
334
+ onResolved();
335
+ } else {
336
+ _onResolvedListeners.push_back(onResolved);
337
+ }
338
+ }
339
+ void addOnRejectedListener(OnRejectedFunc&& onRejected) {
340
+ std::unique_lock lock(*_mutex);
341
+ if (_error.has_value()) {
342
+ onRejected(_error.value());
343
+ } else {
344
+ // Promise is not yet rejected, put the listener in our queue.
345
+ _onRejectedListeners.push_back(std::move(onRejected));
346
+ }
347
+ }
348
+ void addOnRejectedListener(const OnRejectedFunc& onRejected) {
349
+ std::unique_lock lock(*_mutex);
350
+ if (_error.has_value()) {
351
+ onRejected(_error.value());
352
+ } else {
353
+ // Promise is not yet rejected, put the listener in our queue.
354
+ _onRejectedListeners.push_back(onRejected);
355
+ }
356
+ }
357
+
358
+ public:
359
+ inline const TError& getError() {
360
+ if (!isRejected()) {
361
+ throw std::runtime_error("Cannot get error when Promise is not yet rejected!");
362
+ }
363
+ return _error.value();
364
+ }
365
+
366
+ public:
367
+ [[nodiscard]]
368
+ inline bool isResolved() const noexcept {
369
+ return _isResolved;
370
+ }
371
+ [[nodiscard]]
372
+ inline bool isRejected() const noexcept {
373
+ return _error.has_value();
374
+ }
375
+ [[nodiscard]]
376
+ inline bool isPending() const noexcept {
377
+ return !isResolved() && !isRejected();
378
+ }
379
+
380
+ private:
381
+ std::unique_ptr<std::mutex> _mutex;
382
+ bool _isResolved = false;
383
+ std::optional<TError> _error;
384
+ std::vector<OnResolvedFunc> _onResolvedListeners;
385
+ std::vector<OnRejectedFunc> _onRejectedListeners;
386
+ };
387
+
388
+ } // namespace margelo::nitro
@@ -0,0 +1,44 @@
1
+ //
2
+ // Created by Marc Rousavy on 21.02.24.
3
+ //
4
+
5
+ #pragma once
6
+
7
+ // Forward declare a few of the common types that might have cyclic includes.
8
+ namespace margelo::nitro {
9
+ template <typename T, typename Enable>
10
+ struct JSIConverter;
11
+ } // namespace margelo::nitro
12
+
13
+ #include "JSIConverter.hpp"
14
+
15
+ #include <exception>
16
+ #include <jsi/jsi.h>
17
+
18
+ namespace margelo::nitro {
19
+
20
+ using namespace facebook;
21
+
22
+ // std::exception <> Error
23
+ template <>
24
+ struct JSIConverter<std::exception> final {
25
+ static inline std::exception fromJSI(jsi::Runtime& runtime, const jsi::Value& error) {
26
+ jsi::Object object = error.asObject(runtime);
27
+ std::string name = object.getProperty(runtime, "name").asString(runtime).utf8(runtime);
28
+ std::string message = object.getProperty(runtime, "message").asString(runtime).utf8(runtime);
29
+ return std::runtime_error(name + ": " + message);
30
+ }
31
+ static inline jsi::Value toJSI(jsi::Runtime& runtime, const std::exception& exception) {
32
+ jsi::JSError error(runtime, exception.what());
33
+ return jsi::Value(runtime, error.value());
34
+ }
35
+ static inline bool canConvert(jsi::Runtime& runtime, const jsi::Value& value) {
36
+ if (!value.isObject()) {
37
+ return false;
38
+ }
39
+ jsi::Object object = value.getObject(runtime);
40
+ return object.hasProperty(runtime, "name") && object.hasProperty(runtime, "message");
41
+ }
42
+ };
43
+
44
+ } // namespace margelo::nitro
@@ -0,0 +1,45 @@
1
+ //
2
+ // Created by Marc Rousavy on 21.02.24.
3
+ //
4
+
5
+ #pragma once
6
+
7
+ // Forward declare a few of the common types that might have cyclic includes.
8
+ namespace margelo::nitro {
9
+
10
+ template <typename T, typename Enable>
11
+ struct JSIConverter;
12
+ } // namespace margelo::nitro
13
+
14
+ #include "JSIConverter.hpp"
15
+
16
+ #include "Promise.hpp"
17
+ #include <future>
18
+ #include <jsi/jsi.h>
19
+ #include <memory>
20
+
21
+ namespace margelo::nitro {
22
+
23
+ using namespace facebook;
24
+
25
+ // std::future<T> <> Promise<T>
26
+ template <typename TResult>
27
+ struct JSIConverter<std::future<TResult>> final {
28
+ [[deprecated("Use JSIConverter<std::shared_ptr<Promise<T>>> instead.")]]
29
+ static inline std::future<TResult> fromJSI(jsi::Runtime&, const jsi::Value&) {
30
+ throw std::runtime_error("Promise cannot be converted to a native type - it needs to be awaited first!");
31
+ }
32
+
33
+ [[deprecated("Use JSIConverter<std::shared_ptr<Promise<T>>> instead.")]]
34
+ static inline jsi::Value toJSI(jsi::Runtime& runtime, std::future<TResult>&& arg) {
35
+ auto promise = Promise<TResult>::awaitFuture(std::move(arg));
36
+ return JSIConverter<std::shared_ptr<Promise<TResult>>>::toJSI(runtime, promise);
37
+ }
38
+
39
+ [[deprecated("Use JSIConverter<std::shared_ptr<Promise<T>>> instead.")]]
40
+ static inline bool canConvert(jsi::Runtime&, const jsi::Value&) {
41
+ throw std::runtime_error("jsi::Value of type Promise cannot be converted to std::future yet!");
42
+ }
43
+ };
44
+
45
+ } // namespace margelo::nitro
@@ -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)>>::toJSI(
41
+ runtime, [=](const std::exception& 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)>>::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>::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"
@@ -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