@@ -18,7 +18,7 @@ public protocol JSClosureProtocol: JSValueCompatible {
1818public class JSOneshotClosure : JSObject , JSClosureProtocol {
1919 private var hostFuncRef : JavaScriptHostFuncRef = 0
2020
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 ) {
2222 // 1. Fill `id` as zero at first to access `self` to get `ObjectIdentifier`.
2323 super. init ( id: 0 )
2424
@@ -44,11 +44,34 @@ public class JSOneshotClosure: JSObject, JSClosureProtocol {
4444 }
4545
4646 #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.
4752 @available ( macOS 10 . 15 , iOS 13 . 0 , watchOS 6 . 0 , tvOS 13 . 0 , * )
4853 public static func async (
54+ priority: TaskPriority ? = nil ,
55+ file: String = #fileID, line: UInt32 = #line,
4956 _ body: sending @escaping ( sending [ JSValue ] ) async throws ( JSException) -> JSValue
5057 ) -> 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) )
5275 }
5376 #endif
5477
@@ -117,7 +140,7 @@ public class JSClosure: JSFunction, JSClosureProtocol {
117140 } )
118141 }
119142
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 ) {
121144 // 1. Fill `id` as zero at first to access `self` to get `ObjectIdentifier`.
122145 super. init ( id: 0 )
123146
@@ -137,11 +160,34 @@ public class JSClosure: JSFunction, JSClosureProtocol {
137160 }
138161
139162 #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.
140168 @available ( macOS 10 . 15 , iOS 13 . 0 , watchOS 6 . 0 , tvOS 13 . 0 , * )
141169 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
143189 ) -> JSClosure {
144- JSClosure ( makeAsyncClosure ( body) )
190+ JSClosure ( file : file , line : line , makeAsyncClosure ( executorPreference : taskExecutor , priority : priority , body) )
145191 }
146192 #endif
147193
@@ -157,6 +203,36 @@ public class JSClosure: JSFunction, JSClosureProtocol {
157203#if compiler(>=5.5) && (!hasFeature(Embedded) || os(WASI))
158204@available ( macOS 10 . 15 , iOS 13 . 0 , watchOS 6 . 0 , tvOS 13 . 0 , * )
159205private 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 ? ,
160236 _ body: sending @escaping ( sending [ JSValue ] ) async throws ( JSException) -> JSValue
161237) -> ( ( sending [ JSValue ] ) -> JSValue ) {
162238 { arguments in
@@ -170,7 +246,7 @@ private func makeAsyncClosure(
170246 let body : ( sending [ JSValue ] ) async throws ( JSException) -> JSValue
171247 }
172248 let context = Context ( resolver: resolver, arguments: arguments, body: body)
173- Task {
249+ Task ( executorPreference : taskExecutor , priority : priority ) {
174250 do throws ( JSException) {
175251 let result = try await context. body ( context. arguments)
176252 context. resolver ( . success( result) )
0 commit comments