@@ -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