Skip to content

Commit e179d2f

Browse files
committed
fix: Try to use simple Promise approach... fails
1 parent 6eebe9c commit e179d2f

File tree

9 files changed

+176
-63
lines changed

9 files changed

+176
-63
lines changed

package/cpp/JSCallInvoker.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,6 @@ namespace RNWorklet {
1212

1313
using namespace facebook;
1414

15-
using JSCallInvoker = std::function<void(std::function<void(jsi::Runtime& targetRuntime)>&&)>;
15+
using JSCallInvoker = const std::function<void(std::function<void(jsi::Runtime& targetRuntime)>&&)>;
1616

1717
} // namespace RNWorklet

package/cpp/WKTFunctionInvoker.cpp

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,9 @@ void FunctionInvoker::callAndForget(jsi::Runtime& fromRuntime,
8080
const jsi::Value& thisValue,
8181
const jsi::Value* arguments,
8282
size_t count,
83-
JSCallInvoker&& runOnTargetRuntime) {
83+
JSCallInvoker&& runOnTargetRuntime,
84+
std::function<void(std::shared_ptr<JsiWrapper> result)>&& resolve,
85+
std::function<void(std::exception exception)>&& reject) {
8486
// Start by wrapping the arguments
8587
ArgumentsWrapper argsWrapper(fromRuntime, arguments, count);
8688

@@ -90,6 +92,8 @@ void FunctionInvoker::callAndForget(jsi::Runtime& fromRuntime,
9092
std::shared_ptr<FunctionInvoker> self = shared_from_this();
9193
runOnTargetRuntime([self,
9294
thisWrapper,
95+
resolve = std::move(resolve),
96+
reject = std::move(reject),
9397
argsWrapper = std::move(argsWrapper)
9498
](jsi::Runtime& targetRuntime) {
9599
// Now we are on the target Runtime, let's unwrap all arguments and extract them into this target Runtime.
@@ -98,23 +102,37 @@ void FunctionInvoker::callAndForget(jsi::Runtime& fromRuntime,
98102

99103
try {
100104
// Call the actual function or worklet
105+
jsi::Value result;
101106
if (self->isWorkletFunction()) {
102107
// It's a Worklet, so we need to inject the captured values and call it as a Worklet
103108
auto workletInvoker = self->_workletFunctionOrNull;
104-
workletInvoker->call(targetRuntime, unwrappedThis, ArgumentsWrapper::toArgs(args), argsWrapper.getCount());
109+
result = workletInvoker->call(targetRuntime, unwrappedThis, ArgumentsWrapper::toArgs(args), argsWrapper.getCount());
105110
} else {
106111
// It's a normal JS func, so we just call it.
107112
auto plainFunction = self->_plainFunctionOrNull;
108113
if (unwrappedThis.isObject()) {
109114
// ...with `this`
110-
plainFunction->callWithThis(targetRuntime, unwrappedThis.asObject(targetRuntime), ArgumentsWrapper::toArgs(args), argsWrapper.getCount());
115+
result = plainFunction->callWithThis(targetRuntime, unwrappedThis.asObject(targetRuntime), ArgumentsWrapper::toArgs(args), argsWrapper.getCount());
111116
} else {
112117
// ...without `this`
113-
plainFunction->call(targetRuntime, ArgumentsWrapper::toArgs(args), argsWrapper.getCount());
118+
result = plainFunction->call(targetRuntime, ArgumentsWrapper::toArgs(args), argsWrapper.getCount());
114119
}
115120
}
116-
} catch (...) {
117-
// ignore errors.
121+
122+
// Resolve the promise (potentially on the calling Thread)
123+
std::shared_ptr<JsiWrapper> wrapper = JsiWrapper::wrap(targetRuntime, result);
124+
resolve(wrapper);
125+
} catch (std::exception& exception) {
126+
// Reject the promise (potentially on the calling Thread)
127+
reject(exception);
128+
129+
#if DEBUG
130+
// TODO: Remove this?
131+
std::string message = exception.what();
132+
jsi::Object console = targetRuntime.global().getPropertyAsObject(targetRuntime, "console");
133+
jsi::Function errorFn = console.getPropertyAsFunction(targetRuntime, "error");
134+
errorFn.call(targetRuntime, jsi::String::createFromUtf8(targetRuntime, "Worklet threw an error:"), jsi::String::createFromUtf8(targetRuntime, message));
135+
#endif
118136
}
119137
});
120138
}

package/cpp/WKTFunctionInvoker.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,9 @@ class FunctionInvoker: public std::enable_shared_from_this<FunctionInvoker> {
5656
const jsi::Value& thisValue,
5757
const jsi::Value* arguments,
5858
size_t count,
59-
JSCallInvoker&& runOnTargetRuntime);
59+
JSCallInvoker&& runOnTargetRuntime,
60+
std::function<void(std::shared_ptr<JsiWrapper> result)>&& resolve,
61+
std::function<void(std::exception exception)>&& reject);
6062

6163
/**
6264
Calls the underlying function or worklet on the given worklet dispatcher.

package/cpp/WKTJsiPromise.cpp

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
//
2+
// WKTJsiPromise.cpp
3+
// react-native-worklets-core
4+
//
5+
// Created by Marc Rousavy on 16.05.24.
6+
//
7+
8+
#include "WKTJsiPromise.h"
9+
#include "WKTJsiWorkletContext.h"
10+
11+
namespace RNWorklet {
12+
13+
jsi::Value JsiPromise::createPromise(jsi::Runtime& runtime, JSCallInvoker runOnMainJSRuntime, RunPromiseCallback&& run) {
14+
// C++ equivalent of the following JS Code:
15+
// return new Promise((resolve, reject) => {
16+
// run(resolve, reject)
17+
// })
18+
19+
jsi::Function promiseCtor = runtime.global().getPropertyAsFunction(runtime, "Promise");
20+
jsi::HostFunctionType runCtor = [run = std::move(run), runOnMainJSRuntime](jsi::Runtime& runtime,
21+
const jsi::Value& thisValue,
22+
const jsi::Value* arguments,
23+
size_t count) -> jsi::Value {
24+
std::shared_ptr<jsi::Function> resolve = std::make_shared<jsi::Function>(arguments[0].asObject(runtime).asFunction(runtime));
25+
std::shared_ptr<jsi::Function> reject = std::make_shared<jsi::Function>(arguments[1].asObject(runtime).asFunction(runtime));
26+
27+
Resolver resolveWrapper;
28+
Rejecter rejectWrapper;
29+
30+
JsiWorkletContext* callingContext = JsiWorkletContext::getCurrent(runtime);
31+
if (callingContext != nullptr) {
32+
// This is being called from a Worklet Context.
33+
// We need to resolve/reject on this context.
34+
std::weak_ptr<JsiWorkletContext> weakContext = callingContext->shared_from_this();
35+
resolveWrapper = [weakContext, resolve](std::shared_ptr<JsiWrapper> wrappedResult) {
36+
auto context = weakContext.lock();
37+
if (context == nullptr) {
38+
throw std::runtime_error("Cannot resolve Promise - calling Worklet Context has already been destroyed!");
39+
}
40+
context->invokeOnWorkletThread([wrappedResult, resolve](JsiWorkletContext*, jsi::Runtime& runtime) {
41+
jsi::Value result = wrappedResult->unwrap(runtime);
42+
resolve->call(runtime, result);
43+
});
44+
};
45+
rejectWrapper = [weakContext, reject](std::exception error) {
46+
auto context = weakContext.lock();
47+
if (context == nullptr) {
48+
throw std::runtime_error("Cannot resolve Promise - calling Worklet Context has already been destroyed!");
49+
}
50+
context->invokeOnWorkletThread([error](JsiWorkletContext*, jsi::Runtime& runtime) {
51+
// TODO: Stack!!
52+
jsi::JSError(runtime, error.what());
53+
});
54+
};
55+
} else {
56+
// This is being called from the main JS Context.
57+
// We need to resolve/reject on this context.
58+
resolveWrapper = [resolve, runOnMainJSRuntime](std::shared_ptr<JsiWrapper> wrappedResult) {
59+
runOnMainJSRuntime([wrappedResult, resolve](jsi::Runtime& runtime) {
60+
jsi::Value result = wrappedResult->unwrap(runtime);
61+
resolve->call(runtime, result);
62+
});
63+
};
64+
rejectWrapper = [reject, runOnMainJSRuntime](std::exception error) {
65+
runOnMainJSRuntime([error](jsi::Runtime& runtime) {
66+
// TODO: Stack!!
67+
jsi::JSError(runtime, error.what());
68+
});
69+
};
70+
}
71+
72+
run(runtime, resolveWrapper, rejectWrapper);
73+
return jsi::Value::undefined();
74+
};
75+
76+
jsi::Function argument = jsi::Function::createFromHostFunction(runtime, jsi::PropNameID::forUtf8(runtime, "run"), 2, runCtor);
77+
return promiseCtor.callAsConstructor(runtime, argument);
78+
}
79+
80+
} // namespace RNWorklets

package/cpp/WKTJsiPromise.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
//
2+
// WKTJsiPromise.h
3+
// react-native-worklets-core
4+
//
5+
// Created by Marc Rousavy on 16.05.24.
6+
//
7+
8+
#pragma once
9+
10+
#include <jsi/jsi.h>
11+
#include "WKTJsiWrapper.h"
12+
#include "JSCallInvoker.h"
13+
14+
namespace RNWorklet {
15+
16+
using namespace facebook;
17+
18+
class JsiPromise {
19+
public:
20+
using Resolver = std::function<void(std::shared_ptr<JsiWrapper>)>;
21+
using Rejecter = std::function<void(std::exception)>;
22+
using RunPromiseCallback = std::function<void(jsi::Runtime& runtime, Resolver resolve, Rejecter reject)>;
23+
24+
static jsi::Value createPromise(jsi::Runtime& runtime, JSCallInvoker runOnMainJSRuntime, RunPromiseCallback&& run);
25+
};
26+
27+
} // namespace RNWorklets

package/cpp/WKTJsiWorkletApi.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010

1111
#include "WKTJsiHostObject.h"
1212
#include "WKTJsiJsDecorator.h"
13-
#include "WKTJsiPromiseWrapper.h"
1413
#include "WKTJsiSharedValue.h"
1514
#include "WKTJsiWorklet.h"
1615
#include "WKTJsiWorkletContext.h"

package/cpp/WKTJsiWorkletContext.cpp

Lines changed: 40 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "WKTJsiSetImmediateDecorator.h"
1616
#include "WKTJsiJsDecorator.h"
1717
#include "WKTFunctionInvoker.h"
18+
#include "WKTJsiPromise.h"
1819

1920
#include <exception>
2021
#include <functional>
@@ -182,33 +183,24 @@ JsiWorkletContext::createCallOnJS(jsi::Runtime &runtime, const jsi::Value &maybe
182183
const jsi::Value *arguments,
183184
size_t count) -> jsi::Value {
184185
// Runs the given function on the default main JS runtime
185-
auto callOnTargetRuntime = [jsCallInvoker](std::function<void(jsi::Runtime& runtime)> callback) {
186+
JSCallInvoker callOnTargetRuntime = [jsCallInvoker](std::function<void(jsi::Runtime& runtime)> callback) {
186187
jsCallInvoker(std::move(callback));
187188
};
188189

189-
// Runs the given function on the Runtime that originally invoked this method call (to resolve/reject the Promise)
190-
JSCallInvoker callbackToOriginalRuntime;
191-
JsiWorkletContext* currentContext = getCurrent(runtime);
192-
if (currentContext != nullptr) {
193-
// This function is called from a Worklet context, so we wanna schedule back to that Worklet context to resolve the Promise.
194-
std::weak_ptr<JsiWorkletContext> weakContext = currentContext->shared_from_this();
195-
callbackToOriginalRuntime = [weakContext](std::function<void(jsi::Runtime& toRuntime)> callback) {
196-
auto context = weakContext.lock();
197-
if (context == nullptr) [[unlikely]] {
198-
throw std::runtime_error("Cannot call back to JS - the calling context has already been destroyed!");
199-
}
200-
context->invokeOnWorkletThread([callback = std::move(callback)](JsiWorkletContext*, jsi::Runtime& originalRuntime) {
201-
callback(originalRuntime);
202-
});
203-
};
204-
} else {
205-
// This functio nis called from the default JS Context, so we wanna just schedule back to JS to resolve the Promise.
206-
callbackToOriginalRuntime = jsCallInvoker;
207-
}
208-
209-
// Create and run Promise.
210-
std::shared_ptr<JsiPromiseWrapper> promise = invoker->call(runtime, thisValue, arguments, count, std::move(callOnTargetRuntime), std::move(callbackToOriginalRuntime));
211-
return jsi::Object::createFromHostObject(runtime, promise);
190+
// Run function.
191+
auto promise = JsiPromise::createPromise(runtime,
192+
jsCallInvoker,
193+
[invoker,
194+
&thisValue,
195+
&arguments,
196+
&count,
197+
callOnTargetRuntime = std::move(callOnTargetRuntime)
198+
](jsi::Runtime& runtime,
199+
JsiPromise::Resolver resolve,
200+
JsiPromise::Rejecter reject) {
201+
invoker->callAndForget(runtime, thisValue, arguments, count, std::move(callOnTargetRuntime), std::move(resolve), std::move(reject));
202+
});
203+
return promise;
212204
};
213205
}
214206

@@ -229,35 +221,31 @@ jsi::HostFunctionType JsiWorkletContext::createCallInContext(jsi::Runtime &runti
229221
});
230222
};
231223

232-
// Runs the given function on the Runtime that originally invoked this method call (to resolve/reject the Promise)
233-
JSCallInvoker callbackToOriginalRuntime;
234-
JsiWorkletContext* currentContext = getCurrent(runtime);
235-
if (currentContext != nullptr) {
236-
// This function is called from a Worklet context, so we wanna schedule back to that Worklet context to resolve the Promise.
237-
std::weak_ptr<JsiWorkletContext> weakContext = currentContext->shared_from_this();
238-
callbackToOriginalRuntime = [weakContext](std::function<void(jsi::Runtime& toRuntime)> callback) {
239-
auto context = weakContext.lock();
240-
if (context == nullptr) [[unlikely]] {
241-
throw std::runtime_error("Cannot call back to JS - the calling context has already been destroyed!");
242-
}
243-
context->invokeOnWorkletThread([callback = std::move(callback)](JsiWorkletContext*, jsi::Runtime& originalRuntime) {
244-
callback(originalRuntime);
245-
});
246-
};
247-
} else {
248-
// This functio nis called from the default JS Context, so we wanna just schedule back to JS to resolve the Promise.
249-
callbackToOriginalRuntime = [weakSelf](std::function<void(jsi::Runtime& toRuntime)> callback) {
250-
auto self = weakSelf.lock();
251-
if (self == nullptr) [[unlikely]] {
252-
throw std::runtime_error("Cannot call back to JS - the target context has already been destroyed!");
253-
}
254-
self->invokeOnJsThread(std::move(callback));
255-
};
256-
}
257224

258-
// Create and run Promise.
259-
std::shared_ptr<JsiPromiseWrapper> promise = invoker->call(runtime, thisValue, arguments, count, std::move(runOnTargetRuntime), std::move(callbackToOriginalRuntime));
260-
return jsi::Object::createFromHostObject(runtime, promise);
225+
// Run function.
226+
JSCallInvoker jsCallInvoker = [weakSelf](std::function<void(jsi::Runtime& toRuntime)> callback) {
227+
auto self = weakSelf.lock();
228+
if (self == nullptr) [[unlikely]] {
229+
throw std::runtime_error("Cannot call Worklet - the target context has already been destroyed!");
230+
}
231+
self->_jsCallInvoker([self, callback = std::move(callback)]() {
232+
callback(*self->_jsRuntime);
233+
});
234+
};
235+
auto promise = JsiPromise::createPromise(runtime,
236+
jsCallInvoker,
237+
[invoker,
238+
&thisValue,
239+
&arguments,
240+
&count,
241+
runOnTargetRuntime = std::move(runOnTargetRuntime)
242+
](jsi::Runtime& runtime,
243+
JsiPromise::Resolver resolve,
244+
JsiPromise::Rejecter reject) {
245+
invoker->callAndForget(runtime, thisValue, arguments, count, std::move(runOnTargetRuntime), std::move(resolve), std::move(reject));
246+
});
247+
248+
return promise;
261249
};
262250
}
263251

package/cpp/wrappers/WKTJsiObjectWrapper.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
#include <string>
88
#include <vector>
99

10-
#include "WKTJsiPromiseWrapper.h"
1110
#include "WKTJsiWorklet.h"
1211
#include "WKTJsiWrapper.h"
1312

package/src/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ export interface IWorkletNativeApi {
168168
/**
169169
* Get the current Worklet context, or `undefined` if called in main React JS context.
170170
*/
171-
currentContext: IWorkletContext;
171+
currentContext: IWorkletContext | undefined;
172172
/**
173173
* Returns true if jsi/cpp believes that the passed value is an array.
174174
*/

0 commit comments

Comments
 (0)