Skip to content

Commit 7b2edbe

Browse files
Add JSClosure APIs to support specifying TaskExecutor and TaskPriority
1 parent 83e2335 commit 7b2edbe

File tree

1 file changed

+82
-6
lines changed

1 file changed

+82
-6
lines changed

Sources/JavaScriptKit/FundamentalObjects/JSClosure.swift

Lines changed: 82 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public protocol JSClosureProtocol: JSValueCompatible {
1818
public 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, *)
159205
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?,
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

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy