Skip to content

Commit 2f84381

Browse files
Use JSOneshotClosure instead of JSClosure in JSPromise
1 parent 76193c7 commit 2f84381

File tree

1 file changed

+11
-35
lines changed

1 file changed

+11
-35
lines changed

Diff for: Sources/JavaScriptKit/BasicObjects/JSPromise.swift

+11-35
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,11 @@ is of actual JavaScript `Error` type, you should use `JSPromise<JSValue, JSValue
88
This doesn't 100% match the JavaScript API, as `then` overload with two callbacks is not available.
99
It's impossible to unify success and failure types from both callbacks in a single returned promise
1010
without 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
*/
1912
public 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

8065
extension 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

Comments
 (0)