@@ -18,7 +18,7 @@ public protocol JSClosureProtocol: JSValueCompatible {
18
18
public class JSOneshotClosure : JSObject , JSClosureProtocol {
19
19
private var hostFuncRef : JavaScriptHostFuncRef = 0
20
20
21
- public init ( _ body : @escaping ( sending [ JSValue ] ) -> JSValue , file: String = #fileID, line: UInt32 = #line) {
21
+ public init ( file: String = #fileID, line: UInt32 = #line, _ body : @escaping ( sending [ JSValue ] ) -> JSValue ) {
22
22
// 1. Fill `id` as zero at first to access `self` to get `ObjectIdentifier`.
23
23
super. init ( id: 0 )
24
24
@@ -44,11 +44,34 @@ public class JSOneshotClosure: JSObject, JSClosureProtocol {
44
44
}
45
45
46
46
#if compiler(>=5.5) && (!hasFeature(Embedded) || os(WASI))
47
+ /// Creates a new `JSOneshotClosure` that calls the given Swift function asynchronously.
48
+ ///
49
+ /// - Parameters:
50
+ /// - priority: The priority of the new unstructured Task created under the hood.
51
+ /// - body: The Swift function to call asynchronously.
47
52
@available ( macOS 10 . 15 , iOS 13 . 0 , watchOS 6 . 0 , tvOS 13 . 0 , * )
48
53
public static func async (
54
+ priority: TaskPriority ? = nil ,
55
+ file: String = #fileID, line: UInt32 = #line,
49
56
_ body: sending @escaping ( sending [ JSValue ] ) async throws ( JSException) -> JSValue
50
57
) -> JSOneshotClosure {
51
- JSOneshotClosure ( makeAsyncClosure ( body) )
58
+ JSOneshotClosure ( file: file, line: line, makeAsyncClosure ( priority: priority, body) )
59
+ }
60
+
61
+ /// Creates a new `JSOneshotClosure` that calls the given Swift function asynchronously.
62
+ ///
63
+ /// - Parameters:
64
+ /// - taskExecutor: The executor preference of the new unstructured Task created under the hood.
65
+ /// - priority: The priority of the new unstructured Task created under the hood.
66
+ /// - body: The Swift function to call asynchronously.
67
+ @available ( macOS 15 . 0 , iOS 18 . 0 , watchOS 11 . 0 , tvOS 18 . 0 , visionOS 2 . 0 , * )
68
+ public static func async (
69
+ executorPreference taskExecutor: ( any TaskExecutor ) ? = nil ,
70
+ priority: TaskPriority ? = nil ,
71
+ file: String = #fileID, line: UInt32 = #line,
72
+ _ body: @Sendable @escaping ( sending [ JSValue] ) async throws ( JSException ) -> JSValue
73
+ ) -> JSOneshotClosure {
74
+ JSOneshotClosure ( file: file, line: line, makeAsyncClosure ( executorPreference: taskExecutor, priority: priority, body) )
52
75
}
53
76
#endif
54
77
@@ -117,7 +140,7 @@ public class JSClosure: JSFunction, JSClosureProtocol {
117
140
} )
118
141
}
119
142
120
- public init ( _ body : @escaping ( sending [ JSValue ] ) -> JSValue , file: String = #fileID, line: UInt32 = #line) {
143
+ public init ( file: String = #fileID, line: UInt32 = #line, _ body : @escaping ( sending [ JSValue ] ) -> JSValue ) {
121
144
// 1. Fill `id` as zero at first to access `self` to get `ObjectIdentifier`.
122
145
super. init ( id: 0 )
123
146
@@ -137,11 +160,34 @@ public class JSClosure: JSFunction, JSClosureProtocol {
137
160
}
138
161
139
162
#if compiler(>=5.5) && (!hasFeature(Embedded) || os(WASI))
163
+ /// Creates a new `JSClosure` that calls the given Swift function asynchronously.
164
+ ///
165
+ /// - Parameters:
166
+ /// - priority: The priority of the new unstructured Task created under the hood.
167
+ /// - body: The Swift function to call asynchronously.
140
168
@available ( macOS 10 . 15 , iOS 13 . 0 , watchOS 6 . 0 , tvOS 13 . 0 , * )
141
169
public static func async (
142
- _ body: @Sendable @escaping ( sending [ JSValue] ) async throws ( JSException ) -> JSValue
170
+ priority: TaskPriority ? = nil ,
171
+ file: String = #fileID, line: UInt32 = #line,
172
+ _ body: sending @escaping @isolated ( any) ( sending [ JSValue ] ) async throws ( JSException) -> JSValue
173
+ ) -> JSClosure {
174
+ JSClosure ( file: file, line: line, makeAsyncClosure ( priority: priority, body) )
175
+ }
176
+
177
+ /// Creates a new `JSClosure` that calls the given Swift function asynchronously.
178
+ ///
179
+ /// - Parameters:
180
+ /// - taskExecutor: The executor preference of the new unstructured Task created under the hood.
181
+ /// - priority: The priority of the new unstructured Task created under the hood.
182
+ /// - body: The Swift function to call asynchronously.
183
+ @available ( macOS 15 . 0 , iOS 18 . 0 , watchOS 11 . 0 , tvOS 18 . 0 , visionOS 2 . 0 , * )
184
+ public static func async (
185
+ executorPreference taskExecutor: ( any TaskExecutor ) ? = nil ,
186
+ priority: TaskPriority ? = nil ,
187
+ file: String = #fileID, line: UInt32 = #line,
188
+ _ body: sending @escaping ( sending [ JSValue ] ) async throws ( JSException) -> JSValue
143
189
) -> JSClosure {
144
- JSClosure ( makeAsyncClosure ( body) )
190
+ JSClosure ( file : file , line : line , makeAsyncClosure ( executorPreference : taskExecutor , priority : priority , body) )
145
191
}
146
192
#endif
147
193
@@ -157,6 +203,36 @@ public class JSClosure: JSFunction, JSClosureProtocol {
157
203
#if compiler(>=5.5) && (!hasFeature(Embedded) || os(WASI))
158
204
@available ( macOS 10 . 15 , iOS 13 . 0 , watchOS 6 . 0 , tvOS 13 . 0 , * )
159
205
private func makeAsyncClosure(
206
+ priority: TaskPriority ? ,
207
+ _ body: sending @escaping @isolated ( any) ( sending [ JSValue ] ) async throws ( JSException) -> JSValue
208
+ ) -> ( ( sending [ JSValue ] ) -> JSValue ) {
209
+ { arguments in
210
+ JSPromise { resolver in
211
+ // NOTE: The context is fully transferred to the unstructured task
212
+ // isolation but the compiler can't prove it yet, so we need to
213
+ // use `@unchecked Sendable` to make it compile with the Swift 6 mode.
214
+ struct Context : @unchecked Sendable {
215
+ let resolver : ( JSPromise . Result ) -> Void
216
+ let arguments : [ JSValue ]
217
+ let body : ( sending [ JSValue ] ) async throws ( JSException) -> JSValue
218
+ }
219
+ let context = Context ( resolver: resolver, arguments: arguments, body: body)
220
+ Task ( priority: priority) {
221
+ do throws ( JSException) {
222
+ let result = try await context. body ( context. arguments)
223
+ context. resolver ( . success( result) )
224
+ } catch {
225
+ context. resolver ( . failure( error. thrownValue) )
226
+ }
227
+ }
228
+ } . jsValue ( )
229
+ }
230
+ }
231
+
232
+ @available ( macOS 15 . 0 , iOS 18 . 0 , watchOS 11 . 0 , tvOS 18 . 0 , visionOS 2 . 0 , * )
233
+ private func makeAsyncClosure(
234
+ executorPreference taskExecutor: ( any TaskExecutor ) ? ,
235
+ priority: TaskPriority ? ,
160
236
_ body: sending @escaping ( sending [ JSValue ] ) async throws ( JSException) -> JSValue
161
237
) -> ( ( sending [ JSValue ] ) -> JSValue ) {
162
238
{ arguments in
@@ -170,7 +246,7 @@ private func makeAsyncClosure(
170
246
let body : ( sending [ JSValue ] ) async throws ( JSException) -> JSValue
171
247
}
172
248
let context = Context ( resolver: resolver, arguments: arguments, body: body)
173
- Task {
249
+ Task ( executorPreference : taskExecutor , priority : priority ) {
174
250
do throws ( JSException) {
175
251
let result = try await context. body ( context. arguments)
176
252
context. resolver ( . success( result) )
0 commit comments