Skip to content

Commit 41ff951

Browse files
committed
fix Promise.finally() to return new promise via .then()
1 parent c3dc829 commit 41ff951

File tree

2 files changed

+56
-23
lines changed

2 files changed

+56
-23
lines changed

src/lualib/Promise.ts

Lines changed: 17 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ export class __TS__Promise<T> implements Promise<T> {
4545

4646
private fulfilledCallbacks: Array<PromiseResolve<T>> = [];
4747
private rejectedCallbacks: PromiseReject[] = [];
48-
private finallyCallbacks: Array<() => void> = [];
4948

5049
// @ts-ignore
5150
public [Symbol.toStringTag]: string; // Required to implement interface, no output Lua
@@ -124,16 +123,23 @@ export class __TS__Promise<T> implements Promise<T> {
124123
}
125124

126125
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/finally
126+
// Delegates to .then() so that a new Promise is returned (per ES spec §27.2.5.3)
127+
// and the original fulfillment value / rejection reason is preserved.
127128
public finally(onFinally?: () => void): Promise<T> {
128-
if (onFinally) {
129-
this.finallyCallbacks.push(onFinally);
130-
131-
if (this.state !== PromiseState.Pending) {
132-
// If promise already resolved or rejected, immediately fire finally callback
133-
onFinally();
134-
}
135-
}
136-
return this;
129+
return this.then(
130+
onFinally
131+
? (value: T): T => {
132+
onFinally();
133+
return value;
134+
}
135+
: undefined,
136+
onFinally
137+
? (reason: any): never => {
138+
onFinally();
139+
throw reason;
140+
}
141+
: undefined
142+
);
137143
}
138144

139145
private resolve(value: T | PromiseLike<T>): void {
@@ -168,25 +174,13 @@ export class __TS__Promise<T> implements Promise<T> {
168174

169175
private invokeCallbacks<T>(callbacks: ReadonlyArray<(value: T) => void>, value: T): void {
170176
const callbacksLength = callbacks.length;
171-
const finallyCallbacks = this.finallyCallbacks;
172-
const finallyCallbacksLength = finallyCallbacks.length;
173177

174178
if (callbacksLength !== 0) {
175179
for (const i of $range(1, callbacksLength - 1)) {
176180
callbacks[i - 1](value);
177181
}
178182
// Tail call optimization for a common case.
179-
if (finallyCallbacksLength === 0) {
180-
return callbacks[callbacksLength - 1](value);
181-
}
182-
callbacks[callbacksLength - 1](value);
183-
}
184-
185-
if (finallyCallbacksLength !== 0) {
186-
for (const i of $range(1, finallyCallbacksLength - 1)) {
187-
finallyCallbacks[i - 1]();
188-
}
189-
return finallyCallbacks[finallyCallbacksLength - 1]();
183+
return callbacks[callbacksLength - 1](value);
190184
}
191185
}
192186

test/unit/builtins/promise.spec.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1323,3 +1323,42 @@ describe("Promise.race", () => {
13231323
});
13241324
});
13251325
});
1326+
1327+
// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1659
1328+
describe("Promise.finally returns new promise", () => {
1329+
test("finally returns a different promise instance", () => {
1330+
util.testFunction`
1331+
const p1 = new Promise(() => {});
1332+
const p2 = p1.finally();
1333+
return p1 === p2;
1334+
`.expectToEqual(false);
1335+
});
1336+
1337+
test("finally preserves fulfillment value", () => {
1338+
util.testFunction`
1339+
let result: any;
1340+
Promise.resolve(42).finally(() => {}).then(v => { result = v; });
1341+
return result;
1342+
`.expectToEqual(42);
1343+
});
1344+
1345+
test("finally preserves rejection reason", () => {
1346+
util.testFunction`
1347+
let result: any;
1348+
Promise.reject("error").finally(() => {}).catch(r => { result = r; });
1349+
return result;
1350+
`.expectToEqual("error");
1351+
});
1352+
1353+
test("finally on deferred rejection preserves reason", () => {
1354+
util.testFunction`
1355+
const { promise, reject } = defer<string>();
1356+
let result: any;
1357+
promise.finally(() => {}).catch(r => { result = r; });
1358+
reject("deferred error");
1359+
return result;
1360+
`
1361+
.setTsHeader(promiseTestLib)
1362+
.expectToEqual("deferred error");
1363+
});
1364+
});

0 commit comments

Comments
 (0)