Skip to content

Commit caca865

Browse files
committed
[zones] monkey patch Dexie and handle errors
SQUASHED: AUTO-COMMIT-src-client-lang-lang-zone.js,AUTO-COMMIT-src-external-dexie3.js,AUTO-COMMIT-src-external-dexie.js,AUTO-COMMIT-test-lang-test.js,
1 parent bc588bd commit caca865

File tree

4 files changed

+120
-1
lines changed

4 files changed

+120
-1
lines changed

src/client/lang/lang-zone.js

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import 'lang';
22
import Dexie from "src/external/dexie3.js";
33

4+
const { incrementExpectedAwaits, newPSD, decrementExpectedAwaits } = Dexie.Promise;
5+
46
const global = self;
57

68
function exposeGlobal(key, value) {
@@ -20,8 +22,15 @@ function exposeGlobal(key, value) {
2022
- intended to be exchangeable in case of future standard or library advancements
2123
- <span style='background: red'>!only modify if you know what you are doing and understand the **long-term consequences**!</span>
2224
- currently implemented as a lightweight wrapper around [Dexie.Promise](https://dexie.org/docs/Promise/Promise.PSD)
25+
- Caution: we <span style='background: red'>monkey-patched Dexie</span> to access `incrementExpectedAwaits` and `decrementExpectedAwaits` to correctly detect native await on primitives
26+
2327
MD*/
2428

29+
// check if `incrementExpectedAwaits` and `decrementExpectedAwaits` are available
30+
if (![incrementExpectedAwaits, decrementExpectedAwaits].every(func => typeof func === 'function')) {
31+
throw new Error('Dexie not monkey-patched to expose `incrementExpectedAwaits` and `decrementExpectedAwaits`');
32+
}
33+
2534
const globalZone = (() => {
2635
let zone = Dexie.Promise.PSD;
2736

@@ -33,9 +42,12 @@ const globalZone = (() => {
3342
}
3443
}
3544

45+
3646
return zone;
3747
})();
3848

49+
50+
3951
const Zone = {
4052

4153
get root() { return globalZone; },
@@ -44,8 +56,34 @@ const Zone = {
4456

4557
};
4658

59+
export function withZone(scopeFunc, zoneProps = {}) {
60+
let returnValue;
61+
try {
62+
incrementExpectedAwaits();
63+
64+
newPSD(() => {
65+
returnValue = scopeFunc.call();
66+
}, zoneProps);
67+
68+
return returnValue;
69+
} finally {
70+
if (returnValue && typeof returnValue.then === 'function') {
71+
(async () => {
72+
try {
73+
await returnValue;
74+
} catch(e) {
75+
} finally {
76+
decrementExpectedAwaits();
77+
}
78+
})();
79+
} else {
80+
decrementExpectedAwaits();
81+
}
82+
}
83+
}
84+
4785
function runZoned(fn, { zoneValues } = {} ) {
48-
return Dexie.Promise.newPSD(fn, zoneValues);
86+
return withZone(fn, zoneValues);
4987
}
5088

5189
exposeGlobal('Zone', Zone);

src/external/dexie.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
// =============================================================================
2+
// ========================== MONKEY PATCHED TO ================================
3+
// ========================= PROVIDE ZONE ACCESS ===============================
4+
// =============================================================================
5+
// =============================================================================
6+
// =============================================================================
7+
18
/*
29
* Dexie.js - a minimalistic wrapper for IndexedDB
310
* ===============================================
@@ -888,6 +895,8 @@ props(Promise, {
888895
//task: {get: ()=>task},
889896
newPSD: newScope,
890897
usePSD: usePSD,
898+
incrementExpectedAwaits: incrementExpectedAwaits,
899+
decrementExpectedAwaits: decrementExpectedAwaits,
891900
scheduler: {
892901
get: function () { return asap$1; },
893902
set: function (value) { asap$1 = value; }

src/external/dexie3.js

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/lang-test.js

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,69 @@ describe('Zones', function() {
141141
expect(innerZone).to.have.property('p', 42);
142142
});
143143

144+
it('tracks zones over native await on primitive values', async () => {
145+
let callback;
146+
const prom = new Promise(resolve => callback = resolve);
147+
148+
let innerZone;
149+
async function f3() {
150+
await 42;
151+
assert.strictEqual(Zone.current, innerZone);
152+
callback()
153+
}
154+
async function f2() {
155+
await f3();
156+
}
157+
158+
await runZoned(async () => {
159+
innerZone = Zone.current;
160+
await f2();
161+
});
162+
163+
await prom;
164+
});
165+
166+
it('handles errors correctly', async () => {
167+
let catchCalled = false;
168+
let finallyCalled = false;
169+
170+
const expectedError = new Error('expected')
171+
172+
try {
173+
await runZoned(async () => {
174+
throw expectedError;
175+
});
176+
} catch (e) {
177+
catchCalled = true;
178+
} finally {
179+
finallyCalled = true
180+
}
181+
182+
expect(catchCalled).to.be.true;
183+
expect(finallyCalled).to.be.true;
184+
});
185+
186+
it('handles errors correctly after native await on a primitive', async () => {
187+
let catchCalled = false;
188+
let finallyCalled = false;
189+
190+
const expectedError = new Error('expected')
191+
192+
try {
193+
await runZoned(async () => {
194+
await 42;
195+
throw expectedError;
196+
});
197+
} catch (e) {
198+
catchCalled = true;
199+
} finally {
200+
finallyCalled = true
201+
}
202+
203+
expect(catchCalled).to.be.true;
204+
expect(finallyCalled).to.be.true;
205+
});
206+
144207
});
145208

146209
});

0 commit comments

Comments
 (0)