Comments (6)
If you check the call stack, in all cases where it errors the top-level of the call stack will be in dexie's get
function. Any function running under this scope will have Promise
overridden with the DexiePromise
object.
From the dexie Promise
documentation:
window.Promise is always safe to use within transactions, as Dexie will patch the global Promise within the transaction zone, but leave it untouched outside the zone.
Basically, this code is calling the promise resolver while still within the transaction zone:
cactbot/ui/raidboss/emulator/data/Persistor.ts
Lines 61 to 67 in 31ef788
from cactbot.
This is happening because the current zone when importing encounters from the indexedDB is within the dexie scope, so dexie's Promise
is overriding the built-in Promise
.
I believe the correct resolution for this would be to escape that zone after fetching the encounter from Persistor
, in the two cases where it's relevant in raidemulator.ts
(lines 222 and 233). I'm not sure exactly how to go about this other than wrapping the if (enc)
block in a setTimeout
or something, though, and that seems like a pretty ugly solution.
from cactbot.
Oh, I see. Dexie overwrites the Promise
global object (as well as the Promise
constructor even on the previous object???), and so Promise.resolve
is really DexiePromise.resolve
and it wraps a native promise in a DexiePromise
. Normally Promise.resolve
returns the original object if it's instanceof Promise
. DexiePromise.resolve
does the same but only for DexiePromise
objects. Triggers with delays get an extra constructed delay promise that wraps everything else and so skip this issue. I ran a normal log through this and see plenty of StartsUsing
trigger errors as well, so there isn't anything special there.
If I breakpoint the Promise.resolve(promise)
line in popup-text.js
, it seems to be fine while going through different character perspectives (I see a lot of Loading id souma_test
in the console) and then at some point the Promise
global object gets overwritten. It's unclear to me when this happens as dexie.mjs
seems to run that code immediately. It seems like really bad form to overwrite the global Promise
object but it looks like even current versions of Dexie still use this due to IndexedDB promise incompatibility.
One nice thing about checking for Promise
exactly is that we know that both it thennable but also that it is asynchronous. It would be nice to have stronger guarantees about which functions created asynchronicity vs were always synchronous when looking at a cactbot trigger. Unfortunately, there's no way to guarantee that a function is asynchronous without running it and checking that it returns a promise, but promises themselves are guaranteed to be asynchronous.
I guess I see a couple of options:
- explicitly check for
instanceof DexiePromise
as well (like the commit mentioned in the description) - just check that the value of a trigger
promise
field has athen
function and live with (unlikely but possibly) synchronous triggerpromise
fields?
(cc @valarnin)
from cactbot.
public async loadEncounter(id: number): Promise<Encounter | undefined> {
return new Promise<Encounter | undefined>((res) => {
let encounter: Encounter | undefined = undefined;
void this.transaction('readwrite', [this.encounters, this.encounterSummaries], async () => {
encounter = await this.encounters.get(id);
}).then(() => res(encounter));
});
}
What about this? Based on https://dexie.org/docs/Dexie/Dexie.transaction(), it seems like the callback to transaction is in the zone, but if you defer until after the transaction is done then it will no longer be in the zone.
from cactbot.
Maybe this commented version will help with understanding?
public async loadEncounter(id: number): Promise<Encounter | undefined> {
return new Promise<Encounter | undefined>((res) => {
// Start a new transaction with the `encounters` and `encounterSummaries` tables
void this.transaction('readwrite', [this.encounters, this.encounterSummaries],
// Running from within that transaction, so now `window.Promise` has been overwritten with `DexiePromise`
async () => {
// Call `resolve` handler with the result of the `get` for the encounter
// Any code directly resulting from this call is executed within the transaction's scope/zone
res(await this.encounters.get(id));
}
);
});
}
I was thinking something even more simplified as such:
public async loadEncounter(id: number): Promise<Encounter | undefined> {
let enc: Encounter | undefined;
await this.transaction('readwrite', [this.encounters, this.encounterSummaries], async () => {
enc = await this.encounters.get(id);
});
return enc;
}
But I have no time right now to actually test any of these changes.
from cactbot.
I think we're on the same page. That seems like it works. I'll put up a PR.
from cactbot.
Related Issues (20)
- Eden's Promise: Litany - "Watch tethered dog" instruction not helpful HOT 2
- Timeline trouble: in P12S P2, but alway show P1 timeline HOT 9
- Node 20 requires loader parameter again HOT 2
- timeline trouble: in P12S P2 HOT 5
- Follow-up work to player objects (i.e. job names in triggers) HOT 1
- ff14 Korea Ascension4 the latter half doesn't work
- Raidboss Eureka Skoll Howl did not call out HOT 3
- Fishing Overlay not working, log output flooding with message. HOT 1
- The update of the onPartyChange event is unreliable, so player.job may be inaccurate. HOT 4
- Raidboss: questions about migrating Another Aloalo trigger .ts file to savage HOT 5
- make_timeline ignores actors with empty names HOT 2
- Raidboss: Cactbot catching network logs randomly when two logs show at the same time HOT 4
- NodeJS LTS 20 Support HOT 1
- Desync alerts for Cactbot plugin on Weapon's Refrain Ultimate ( UWU ) HOT 5
- make/test timeline has weird offset with log file HOT 2
- Callout request: TEA P2: add warning for water debuff
- Cactbot duty timeline shrinks over time HOT 2
- E8S Mirror Mirror 3 sometimes does wrong directions HOT 3
- Bozja Zadnor CE and DAL/Castrum not showing on map overlay HOT 1
- Cactbot is having a problem with The Unending coil of Bahamut. HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from cactbot.