1- /** A wrapper around [the JavaScript `Promise` class](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)
2- that exposes its functions in a type-safe and Swifty way. The `JSPromise` API is generic over both
3- `Success` and `Failure` types, which improves compatibility with other statically-typed APIs such
4- as Combine. If you don't know the exact type of your `Success` value, you should use `JSValue`, e.g.
5- `JSPromise<JSValue, JSError>`. In the rare case, where you can't guarantee that the error thrown
6- is of actual JavaScript `Error` type, you should use `JSPromise<JSValue, JSValue>`.
7-
8- This doesn't 100% match the JavaScript API, as `then` overload with two callbacks is not available.
9- It's impossible to unify success and failure types from both callbacks in a single returned promise
10- without type erasure. You should chain `then` and `catch` in those cases to avoid type erasure.
11- */
1+ /// A wrapper around [the JavaScript `Promise` class](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)
122public final class JSPromise : JSBridgedClass {
133 /// The underlying JavaScript `Promise` object.
144 public let jsObject : JSObject
@@ -27,25 +17,27 @@ public final class JSPromise: JSBridgedClass {
2717 jsObject = object
2818 }
2919
30- /** Creates a new `JSPromise` instance from a given JavaScript `Promise` object. If `jsObject`
31- is not an instance of JavaScript `Promise`, this initializer will return `nil`.
32- */
20+ /// Creates a new `JSPromise` instance from a given JavaScript `Promise` object. If `jsObject`
21+ /// is not an instance of JavaScript `Promise`, this initializer will return `nil`.
3322 public convenience init ? ( _ jsObject: JSObject ) {
3423 self . init ( from: jsObject)
3524 }
3625
37- /** Creates a new `JSPromise` instance from a given JavaScript `Promise` object. If `value`
38- is not an object and is not an instance of JavaScript `Promise`, this function will
39- return `nil`.
40- */
26+ /// Creates a new `JSPromise` instance from a given JavaScript `Promise` object. If `value`
27+ /// is not an object and is not an instance of JavaScript `Promise`, this function will
28+ /// return `nil`.
4129 public static func construct( from value: JSValue ) -> Self ? {
4230 guard case let . object( jsObject) = value else { return nil }
4331 return Self ( jsObject)
4432 }
4533
46- /** Creates a new `JSPromise` instance from a given `resolver` closure. `resolver` takes
47- two closure that your code should call to either resolve or reject this `JSPromise` instance.
48- */
34+ /// Creates a new `JSPromise` instance from a given `resolver` closure.
35+ /// The closure is passed a completion handler. Passing a successful
36+ /// `Result` to the completion handler will cause the promise to resolve
37+ /// with the corresponding value; passing a failure `Result` will cause the
38+ /// promise to reject with the corresponding value.
39+ /// Calling the completion handler more than once will have no effect
40+ /// (per the JavaScript specification).
4941 public convenience init ( resolver: @escaping ( @escaping ( Result < JSValue , JSValue > ) -> Void ) -> Void ) {
5042 let closure = JSOneshotClosure { arguments in
5143 // The arguments are always coming from the `Promise` constructor, so we should be
@@ -74,8 +66,7 @@ public final class JSPromise: JSBridgedClass {
7466 self . init ( unsafelyWrapping: Self . constructor!. reject!( reason) . object!)
7567 }
7668
77- /** Schedules the `success` closure to be invoked on sucessful completion of `self`.
78- */
69+ /// Schedules the `success` closure to be invoked on successful completion of `self`.
7970 @discardableResult
8071 public func then( success: @escaping ( JSValue ) -> ConvertibleToJSValue ) -> JSPromise {
8172 let closure = JSOneshotClosure {
@@ -84,8 +75,19 @@ public final class JSPromise: JSBridgedClass {
8475 return JSPromise ( unsafelyWrapping: jsObject. then!( closure) . object!)
8576 }
8677
87- /** Schedules the `success` closure to be invoked on sucessful completion of `self`.
88- */
78+ #if compiler(>=5.5)
79+ /// Schedules the `success` closure to be invoked on successful completion of `self`.
80+ @available ( macOS 10 . 15 , iOS 13 . 0 , watchOS 6 . 0 , tvOS 13 . 0 , * )
81+ @discardableResult
82+ public func then( success: @escaping ( JSValue ) async throws -> ConvertibleToJSValue ) -> JSPromise {
83+ let closure = JSOneshotClosure . async {
84+ try await success ( $0 [ 0 ] ) . jsValue
85+ }
86+ return JSPromise ( unsafelyWrapping: jsObject. then!( closure) . object!)
87+ }
88+ #endif
89+
90+ /// Schedules the `success` closure to be invoked on successful completion of `self`.
8991 @discardableResult
9092 public func then(
9193 success: @escaping ( JSValue ) -> ConvertibleToJSValue ,
@@ -100,8 +102,24 @@ public final class JSPromise: JSBridgedClass {
100102 return JSPromise ( unsafelyWrapping: jsObject. then!( successClosure, failureClosure) . object!)
101103 }
102104
103- /** Schedules the `failure` closure to be invoked on rejected completion of `self`.
104- */
105+ #if compiler(>=5.5)
106+ /// Schedules the `success` closure to be invoked on successful completion of `self`.
107+ @available ( macOS 10 . 15 , iOS 13 . 0 , watchOS 6 . 0 , tvOS 13 . 0 , * )
108+ @discardableResult
109+ public func then( success: @escaping ( JSValue ) async throws -> ConvertibleToJSValue ,
110+ failure: @escaping ( JSValue ) async throws -> ConvertibleToJSValue ) -> JSPromise
111+ {
112+ let successClosure = JSOneshotClosure . async {
113+ try await success ( $0 [ 0 ] ) . jsValue
114+ }
115+ let failureClosure = JSOneshotClosure . async {
116+ try await failure ( $0 [ 0 ] ) . jsValue
117+ }
118+ return JSPromise ( unsafelyWrapping: jsObject. then!( successClosure, failureClosure) . object!)
119+ }
120+ #endif
121+
122+ /// Schedules the `failure` closure to be invoked on rejected completion of `self`.
105123 @discardableResult
106124 public func `catch`( failure: @escaping ( JSValue ) -> ConvertibleToJSValue ) -> JSPromise {
107125 let closure = JSOneshotClosure {
@@ -110,9 +128,20 @@ public final class JSPromise: JSBridgedClass {
110128 return . init( unsafelyWrapping: jsObject. catch!( closure) . object!)
111129 }
112130
113- /** Schedules the `failure` closure to be invoked on either successful or rejected completion of
114- `self`.
115- */
131+ #if compiler(>=5.5)
132+ /// Schedules the `failure` closure to be invoked on rejected completion of `self`.
133+ @available ( macOS 10 . 15 , iOS 13 . 0 , watchOS 6 . 0 , tvOS 13 . 0 , * )
134+ @discardableResult
135+ public func `catch`( failure: @escaping ( JSValue ) async throws -> ConvertibleToJSValue ) -> JSPromise {
136+ let closure = JSOneshotClosure . async {
137+ try await failure ( $0 [ 0 ] ) . jsValue
138+ }
139+ return . init( unsafelyWrapping: jsObject. catch!( closure) . object!)
140+ }
141+ #endif
142+
143+ /// Schedules the `failure` closure to be invoked on either successful or rejected
144+ /// completion of `self`.
116145 @discardableResult
117146 public func finally( successOrFailure: @escaping ( ) -> Void ) -> JSPromise {
118147 let closure = JSOneshotClosure { _ in
0 commit comments