Skip to content

Commit 6982b47

Browse files
ikeyanclaude
authored andcommitted
[Fix] AsyncDisposableStack: continue disposal chain after a throwing defer
Fixes #9 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent c0871b3 commit 6982b47

2 files changed

Lines changed: 50 additions & 9 deletions

File tree

aos/DisposeResources.js

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ var $then = callBound('Promise.prototype.then', true);
1111

1212
var CompletionRecord = require('es-abstract/2025/CompletionRecord');
1313
var Dispose = require('./Dispose');
14-
var NormalCompletion = require('es-abstract/2025/NormalCompletion');
1514
var PromiseResolve = require('es-abstract/2025/PromiseResolve');
1615
var ThrowCompletion = require('es-abstract/2025/ThrowCompletion');
1716

@@ -56,9 +55,8 @@ module.exports = function DisposeResources(disposeCapability, completion) {
5655
};
5756

5857
var getPromise = actualHint === 'ASYNC-DISPOSE' && function getPromise(resource) {
59-
return $then(
60-
promise,
61-
function () {
58+
var runDispose = function () {
59+
try {
6260
var result = Dispose( // step 2.a
6361
resource['[[ResourceValue]]'],
6462
resource['[[Hint]]'],
@@ -67,9 +65,19 @@ module.exports = function DisposeResources(disposeCapability, completion) {
6765
if (!result) {
6866
throw new $SyntaxError('Assertion failed: non-`~ASYNC-DISPOSE~` resource returned a promise from Dispose');
6967
}
70-
return $then(result, NormalCompletion);
71-
},
72-
rejecter
68+
return $then(result, void undefined, rejecter);
69+
} catch (e) {
70+
rejecter(e);
71+
}
72+
return void undefined;
73+
};
74+
return $then(
75+
promise,
76+
runDispose,
77+
function (e) {
78+
rejecter(e);
79+
return runDispose();
80+
}
7381
);
7482
};
7583

@@ -96,5 +104,10 @@ module.exports = function DisposeResources(disposeCapability, completion) {
96104
// eslint-disable-next-line no-param-reassign
97105
disposeCapability['[[DisposableResourceStack]]'] = null; // step 3
98106

99-
return actualHint === 'ASYNC-DISPOSE' ? promise : completion; // step 4
107+
if (actualHint === 'ASYNC-DISPOSE') { // step 4
108+
return $then(promise, function () {
109+
return completion;
110+
});
111+
}
112+
return completion;
100113
};

test/tests.js

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -621,7 +621,35 @@ module.exports = {
621621
function (e) {
622622
st.equal(e, throwSentinel, 'throws `throwSentinel`');
623623
}
624-
);
624+
).then(function () {
625+
// https://github.com/es-shims/DisposableStack/issues/9
626+
var instance3 = new AsyncDisposableStack();
627+
var ran = 0;
628+
instance3.defer(function () { ran += 1; });
629+
instance3.defer(throwsSentinel);
630+
631+
return instance3.disposeAsync().then(
632+
function () {
633+
st.fail('dispose with a throwing later disposable failed to throw');
634+
},
635+
function (e) {
636+
st.equal(e, throwSentinel, 'throws `throwSentinel` when an earlier-pushed defer throws');
637+
st.equal(ran, 1, 'later-pushed non-throwing defer still ran');
638+
}
639+
);
640+
}).then(function () {
641+
var instance4 = new AsyncDisposableStack();
642+
instance4.defer(function () { return Promise.reject(throwSentinel); });
643+
644+
return instance4.disposeAsync().then(
645+
function () {
646+
st.fail('dispose with a rejecting disposable failed to throw');
647+
},
648+
function (e) {
649+
st.equal(e, throwSentinel, 'rejecting defer surfaces `throwSentinel`');
650+
}
651+
);
652+
});
625653
});
626654
});
627655

0 commit comments

Comments
 (0)