@@ -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,40 @@ 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,
56
+ line: UInt32 = #line,
49
57
_ body: sending @escaping ( sending [ JSValue ] ) async throws ( JSException) -> JSValue
50
58
) -> JSOneshotClosure {
51
- JSOneshotClosure ( makeAsyncClosure ( body) )
59
+ JSOneshotClosure ( file: file, line: line, makeAsyncClosure ( priority: priority, body) )
60
+ }
61
+
62
+ /// Creates a new `JSOneshotClosure` that calls the given Swift function asynchronously.
63
+ ///
64
+ /// - Parameters:
65
+ /// - taskExecutor: The executor preference of the new unstructured Task created under the hood.
66
+ /// - priority: The priority of the new unstructured Task created under the hood.
67
+ /// - body: The Swift function to call asynchronously.
68
+ @available ( macOS 15 . 0 , iOS 18 . 0 , watchOS 11 . 0 , tvOS 18 . 0 , visionOS 2 . 0 , * )
69
+ public static func async (
70
+ executorPreference taskExecutor: ( any TaskExecutor ) ? = nil ,
71
+ priority: TaskPriority ? = nil ,
72
+ file: String = #fileID,
73
+ line: UInt32 = #line,
74
+ _ body: @Sendable @escaping ( sending [ JSValue] ) async throws ( JSException ) -> JSValue
75
+ ) -> JSOneshotClosure {
76
+ JSOneshotClosure (
77
+ file: file,
78
+ line: line,
79
+ makeAsyncClosure ( executorPreference: taskExecutor, priority: priority, body)
80
+ )
52
81
}
53
82
#endif
54
83
@@ -117,7 +146,7 @@ public class JSClosure: JSFunction, JSClosureProtocol {
117
146
} )
118
147
}
119
148
120
- public init ( _ body : @escaping ( sending [ JSValue ] ) -> JSValue , file: String = #fileID, line: UInt32 = #line) {
149
+ public init ( file: String = #fileID, line: UInt32 = #line, _ body : @escaping ( sending [ JSValue ] ) -> JSValue ) {
121
150
// 1. Fill `id` as zero at first to access `self` to get `ObjectIdentifier`.
122
151
super. init ( id: 0 )
123
152
@@ -137,11 +166,36 @@ public class JSClosure: JSFunction, JSClosureProtocol {
137
166
}
138
167
139
168
#if compiler(>=5.5) && (!hasFeature(Embedded) || os(WASI))
169
+ /// Creates a new `JSClosure` that calls the given Swift function asynchronously.
170
+ ///
171
+ /// - Parameters:
172
+ /// - priority: The priority of the new unstructured Task created under the hood.
173
+ /// - body: The Swift function to call asynchronously.
140
174
@available ( macOS 10 . 15 , iOS 13 . 0 , watchOS 6 . 0 , tvOS 13 . 0 , * )
141
175
public static func async (
142
- _ body: @Sendable @escaping ( sending [ JSValue] ) async throws ( JSException ) -> JSValue
176
+ priority: TaskPriority ? = nil ,
177
+ file: String = #fileID,
178
+ line: UInt32 = #line,
179
+ _ body: sending @escaping @isolated ( any) ( sending [ JSValue ] ) async throws ( JSException) -> JSValue
180
+ ) -> JSClosure {
181
+ JSClosure ( file: file, line: line, makeAsyncClosure ( priority: priority, body) )
182
+ }
183
+
184
+ /// Creates a new `JSClosure` that calls the given Swift function asynchronously.
185
+ ///
186
+ /// - Parameters:
187
+ /// - taskExecutor: The executor preference of the new unstructured Task created under the hood.
188
+ /// - priority: The priority of the new unstructured Task created under the hood.
189
+ /// - body: The Swift function to call asynchronously.
190
+ @available ( macOS 15 . 0 , iOS 18 . 0 , watchOS 11 . 0 , tvOS 18 . 0 , visionOS 2 . 0 , * )
191
+ public static func async (
192
+ executorPreference taskExecutor: ( any TaskExecutor ) ? = nil ,
193
+ priority: TaskPriority ? = nil ,
194
+ file: String = #fileID,
195
+ line: UInt32 = #line,
196
+ _ body: sending @escaping ( sending [ JSValue ] ) async throws ( JSException) -> JSValue
143
197
) -> JSClosure {
144
- JSClosure ( makeAsyncClosure ( body) )
198
+ JSClosure ( file : file , line : line , makeAsyncClosure ( executorPreference : taskExecutor , priority : priority , body) )
145
199
}
146
200
#endif
147
201
@@ -157,6 +211,36 @@ public class JSClosure: JSFunction, JSClosureProtocol {
157
211
#if compiler(>=5.5) && (!hasFeature(Embedded) || os(WASI))
158
212
@available ( macOS 10 . 15 , iOS 13 . 0 , watchOS 6 . 0 , tvOS 13 . 0 , * )
159
213
private func makeAsyncClosure(
214
+ priority: TaskPriority ? ,
215
+ _ body: sending @escaping @isolated ( any) ( sending [ JSValue ] ) async throws ( JSException) -> JSValue
216
+ ) -> ( ( sending [ JSValue ] ) -> JSValue ) {
217
+ { arguments in
218
+ JSPromise { resolver in
219
+ // NOTE: The context is fully transferred to the unstructured task
220
+ // isolation but the compiler can't prove it yet, so we need to
221
+ // use `@unchecked Sendable` to make it compile with the Swift 6 mode.
222
+ struct Context : @unchecked Sendable {
223
+ let resolver : ( JSPromise . Result ) -> Void
224
+ let arguments : [ JSValue ]
225
+ let body : ( sending [ JSValue ] ) async throws ( JSException) -> JSValue
226
+ }
227
+ let context = Context ( resolver: resolver, arguments: arguments, body: body)
228
+ Task ( priority: priority) {
229
+ do throws ( JSException) {
230
+ let result = try await context. body ( context. arguments)
231
+ context. resolver ( . success( result) )
232
+ } catch {
233
+ context. resolver ( . failure( error. thrownValue) )
234
+ }
235
+ }
236
+ } . jsValue ( )
237
+ }
238
+ }
239
+
240
+ @available ( macOS 15 . 0 , iOS 18 . 0 , watchOS 11 . 0 , tvOS 18 . 0 , visionOS 2 . 0 , * )
241
+ private func makeAsyncClosure(
242
+ executorPreference taskExecutor: ( any TaskExecutor ) ? ,
243
+ priority: TaskPriority ? ,
160
244
_ body: sending @escaping ( sending [ JSValue ] ) async throws ( JSException) -> JSValue
161
245
) -> ( ( sending [ JSValue ] ) -> JSValue ) {
162
246
{ arguments in
@@ -170,7 +254,7 @@ private func makeAsyncClosure(
170
254
let body : ( sending [ JSValue ] ) async throws ( JSException) -> JSValue
171
255
}
172
256
let context = Context ( resolver: resolver, arguments: arguments, body: body)
173
- Task {
257
+ Task ( executorPreference : taskExecutor , priority : priority ) {
174
258
do throws ( JSException) {
175
259
let result = try await context. body ( context. arguments)
176
260
context. resolver ( . success( result) )
0 commit comments