@@ -8,20 +8,11 @@ is of actual JavaScript `Error` type, you should use `JSPromise<JSValue, JSValue
88This doesn't 100% match the JavaScript API, as `then` overload with two callbacks is not available.
99It's impossible to unify success and failure types from both callbacks in a single returned promise
1010without type erasure. You should chain `then` and `catch` in those cases to avoid type erasure.
11-
12- **IMPORTANT**: instances of this class must have the same lifetime as the actual `Promise` object in
13- the JavaScript environment, because callback handlers will be deallocated when `JSPromise.deinit` is
14- executed.
15-
16- If the actual `Promise` object in JavaScript environment lives longer than this `JSPromise`, it may
17- attempt to call a deallocated `JSClosure`.
1811*/
1912public final class JSPromise<Success, Failure>: ConvertibleToJSValue, ConstructibleFromJSValue {
2013 /// The underlying JavaScript `Promise` object.
2114 public let jsObject: JSObject
2215
23- private var callbacks = [JSClosure]()
24-
2516 /// The underlying JavaScript `Promise` object wrapped as `JSValue`.
2617 public func jsValue() -> JSValue {
2718 .object(jsObject)
@@ -52,44 +43,37 @@ public final class JSPromise<Success, Failure>: ConvertibleToJSValue, Constructi
5243 /** Schedules the `success` closure to be invoked on sucessful completion of `self`.
5344 */
5445 public func then(success: @escaping () -> ()) {
55- let closure = JSClosure { _ in
46+ let closure = JSOneshotClosure { _ in
5647 success()
5748 return .undefined
5849 }
59- callbacks.append(closure)
6050 _ = jsObject.then!(closure)
6151 }
6252
6353 /** Schedules the `failure` closure to be invoked on either successful or rejected completion of
6454 `self`.
6555 */
6656 public func finally(successOrFailure: @escaping () -> ()) -> Self {
67- let closure = JSClosure { _ in
57+ let closure = JSOneshotClosure { _ in
6858 successOrFailure()
6959 return .undefined
7060 }
71- callbacks.append(closure)
7261 return .init(unsafe: jsObject.finally!(closure).object!)
7362 }
74-
75- deinit {
76- callbacks.forEach { $0.release() }
77- }
7863}
7964
8065extension JSPromise where Success == (), Failure == Never {
8166 /** Creates a new `JSPromise` instance from a given `resolver` closure. `resolver` takes
8267 a closure that your code should call to resolve this `JSPromise` instance.
8368 */
8469 public convenience init(resolver: @escaping (@escaping () -> ()) -> ()) {
85- let closure = JSClosure { arguments in
70+ let closure = JSOneshotClosure { arguments in
8671 // The arguments are always coming from the `Promise` constructor, so we should be
8772 // safe to assume their type here
8873 resolver { arguments[0].function!() }
8974 return .undefined
9075 }
9176 self.init(unsafe: JSObject.global.Promise.function!.new(closure))
92- callbacks.append(closure)
9377 }
9478}
9579
@@ -98,7 +82,7 @@ extension JSPromise where Failure: ConvertibleToJSValue {
9882 two closure that your code should call to either resolve or reject this `JSPromise` instance.
9983 */
10084 public convenience init(resolver: @escaping (@escaping (Result<Success, JSError>) -> ()) -> ()) {
101- let closure = JSClosure { arguments in
85+ let closure = JSOneshotClosure { arguments in
10286 // The arguments are always coming from the `Promise` constructor, so we should be
10387 // safe to assume their type here
10488 let resolve = arguments[0].function!
@@ -115,7 +99,6 @@ extension JSPromise where Failure: ConvertibleToJSValue {
11599 return .undefined
116100 }
117101 self.init(unsafe: JSObject.global.Promise.function!.new(closure))
118- callbacks.append(closure)
119102 }
120103}
121104
@@ -124,7 +107,7 @@ extension JSPromise where Success: ConvertibleToJSValue, Failure: JSError {
124107 a closure that your code should call to either resolve or reject this `JSPromise` instance.
125108 */
126109 public convenience init(resolver: @escaping (@escaping (Result<Success, JSError>) -> ()) -> ()) {
127- let closure = JSClosure { arguments in
110+ let closure = JSOneshotClosure { arguments in
128111 // The arguments are always coming from the `Promise` constructor, so we should be
129112 // safe to assume their type here
130113 let resolve = arguments[0].function!
@@ -141,7 +124,6 @@ extension JSPromise where Success: ConvertibleToJSValue, Failure: JSError {
141124 return .undefined
142125 }
143126 self.init(unsafe: JSObject.global.Promise.function!.new(closure))
144- callbacks.append(closure)
145127 }
146128}
147129
@@ -153,14 +135,13 @@ extension JSPromise where Success: ConstructibleFromJSValue {
153135 file: StaticString = #file,
154136 line: Int = #line
155137 ) {
156- let closure = JSClosure { arguments in
138+ let closure = JSOneshotClosure { arguments in
157139 guard let result = Success.construct(from: arguments[0]) else {
158140 fatalError("\(file):\(line): failed to unwrap success value for `then` callback")
159141 }
160142 success(result)
161143 return .undefined
162144 }
163- callbacks.append(closure)
164145 _ = jsObject.then!(closure)
165146 }
166147
@@ -173,13 +154,12 @@ extension JSPromise where Success: ConstructibleFromJSValue {
173154 file: StaticString = #file,
174155 line: Int = #line
175156 ) -> JSPromise<ResultType, Failure> {
176- let closure = JSClosure { arguments -> JSValue in
157+ let closure = JSOneshotClosure { arguments -> JSValue in
177158 guard let result = Success.construct(from: arguments[0]) else {
178159 fatalError("\(file):\(line): failed to unwrap success value for `then` callback")
179160 }
180161 return success(result).jsValue()
181162 }
182- callbacks.append(closure)
183163 return .init(unsafe: jsObject.then!(closure).object!)
184164 }
185165
@@ -192,13 +172,12 @@ extension JSPromise where Success: ConstructibleFromJSValue {
192172 file: StaticString = #file,
193173 line: Int = #line
194174 ) -> JSPromise<ResultSuccess, ResultFailure> {
195- let closure = JSClosure { arguments -> JSValue in
175+ let closure = JSOneshotClosure { arguments -> JSValue in
196176 guard let result = Success.construct(from: arguments[0]) else {
197177 fatalError("\(file):\(line): failed to unwrap success value for `then` callback")
198178 }
199179 return success(result).jsValue()
200180 }
201- callbacks.append(closure)
202181 return .init(unsafe: jsObject.then!(closure).object!)
203182 }
204183}
@@ -213,13 +192,12 @@ extension JSPromise where Failure: ConstructibleFromJSValue {
213192 file: StaticString = #file,
214193 line: Int = #line
215194 ) -> JSPromise<ResultSuccess, Never> {
216- let closure = JSClosure { arguments -> JSValue in
195+ let closure = JSOneshotClosure { arguments -> JSValue in
217196 guard let error = Failure.construct(from: arguments[0]) else {
218197 fatalError("\(file):\(line): failed to unwrap error value for `catch` callback")
219198 }
220199 return failure(error).jsValue()
221200 }
222- callbacks.append(closure)
223201 return .init(unsafe: jsObject.then!(JSValue.undefined, closure).object!)
224202 }
225203
@@ -230,14 +208,13 @@ extension JSPromise where Failure: ConstructibleFromJSValue {
230208 file: StaticString = #file,
231209 line: Int = #line
232210 ) {
233- let closure = JSClosure { arguments in
211+ let closure = JSOneshotClosure { arguments in
234212 guard let error = Failure.construct(from: arguments[0]) else {
235213 fatalError("\(file):\(line): failed to unwrap error value for `catch` callback")
236214 }
237215 failure(error)
238216 return .undefined
239217 }
240- callbacks.append(closure)
241218 _ = jsObject.then!(JSValue.undefined, closure)
242219 }
243220
@@ -250,13 +227,12 @@ extension JSPromise where Failure: ConstructibleFromJSValue {
250227 file: StaticString = #file,
251228 line: Int = #line
252229 ) -> JSPromise<ResultSuccess, ResultFailure> {
253- let closure = JSClosure { arguments -> JSValue in
230+ let closure = JSOneshotClosure { arguments -> JSValue in
254231 guard let error = Failure.construct(from: arguments[0]) else {
255232 fatalError("\(file):\(line): failed to unwrap error value for `catch` callback")
256233 }
257234 return failure(error).jsValue()
258235 }
259- callbacks.append(closure)
260236 return .init(unsafe: jsObject.then!(JSValue.undefined, closure).object!)
261237 }
262238}
0 commit comments