@@ -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,40 @@ 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,
56+ line: UInt32 = #line,
4957 _ body: sending @escaping ( sending [ JSValue ] ) async throws ( JSException) -> JSValue
5058 ) -> 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+ )
5281 }
5382 #endif
5483
@@ -117,7 +146,7 @@ public class JSClosure: JSFunction, JSClosureProtocol {
117146 } )
118147 }
119148
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 ) {
121150 // 1. Fill `id` as zero at first to access `self` to get `ObjectIdentifier`.
122151 super. init ( id: 0 )
123152
@@ -137,11 +166,36 @@ public class JSClosure: JSFunction, JSClosureProtocol {
137166 }
138167
139168 #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.
140174 @available ( macOS 10 . 15 , iOS 13 . 0 , watchOS 6 . 0 , tvOS 13 . 0 , * )
141175 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
143197 ) -> JSClosure {
144- JSClosure ( makeAsyncClosure ( body) )
198+ JSClosure ( file : file , line : line , makeAsyncClosure ( executorPreference : taskExecutor , priority : priority , body) )
145199 }
146200 #endif
147201
@@ -157,6 +211,36 @@ public class JSClosure: JSFunction, JSClosureProtocol {
157211#if compiler(>=5.5) && (!hasFeature(Embedded) || os(WASI))
158212@available ( macOS 10 . 15 , iOS 13 . 0 , watchOS 6 . 0 , tvOS 13 . 0 , * )
159213private 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 ? ,
160244 _ body: sending @escaping ( sending [ JSValue ] ) async throws ( JSException) -> JSValue
161245) -> ( ( sending [ JSValue ] ) -> JSValue ) {
162246 { arguments in
@@ -170,7 +254,7 @@ private func makeAsyncClosure(
170254 let body : ( sending [ JSValue ] ) async throws ( JSException) -> JSValue
171255 }
172256 let context = Context ( resolver: resolver, arguments: arguments, body: body)
173- Task {
257+ Task ( executorPreference : taskExecutor , priority : priority ) {
174258 do throws ( JSException) {
175259 let result = try await context. body ( context. arguments)
176260 context. resolver ( . success( result) )
0 commit comments