react-native-nitro-modules 0.16.1 → 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.
- package/NitroModules.podspec +2 -1
- package/README.md +6 -6
- package/android/build.gradle +10 -1
- package/android/src/main/cpp/core/JPromise.hpp +62 -5
- package/android/src/main/cpp/utils/JUnit.hpp +38 -0
- package/android/src/main/java/com/margelo/nitro/core/Promise.kt +76 -10
- package/cpp/core/HybridFunction.hpp +1 -0
- package/cpp/core/Promise.cpp +10 -0
- package/cpp/core/Promise.hpp +388 -0
- package/cpp/jsi/JSIConverter+Exception.hpp +44 -0
- package/cpp/jsi/JSIConverter+Future.hpp +45 -0
- package/cpp/jsi/JSIConverter+Promise.hpp +76 -62
- package/cpp/jsi/JSIConverter.hpp +2 -0
- package/cpp/prototype/HybridObjectPrototype.cpp +1 -0
- package/cpp/threading/ThreadPool.cpp +47 -32
- package/cpp/threading/ThreadPool.hpp +15 -3
- package/ios/core/RuntimeError.swift +18 -0
- package/ios/utils/RuntimeError.hpp +23 -0
- package/ios/utils/SwiftClosure.hpp +5 -1
- package/package.json +2 -2
- package/cpp/jsi/JSPromise.cpp +0 -58
- package/cpp/jsi/JSPromise.hpp +0 -54
- package/ios/core/PromiseHolder.hpp +0 -86
|
@@ -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 "
|
|
20
|
-
#include
|
|
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::
|
|
25
|
+
// Promise<T, std::exception> <> Promise<T>
|
|
32
26
|
template <typename TResult>
|
|
33
|
-
struct JSIConverter<std::
|
|
34
|
-
static inline std::
|
|
35
|
-
|
|
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::
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
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
|
|
90
|
-
|
|
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
|
|
package/cpp/jsi/JSIConverter.hpp
CHANGED
|
@@ -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"
|