-
Notifications
You must be signed in to change notification settings - Fork 119
Block while parsing scripts to prevent sending 'removes' before 'adds' #274
Block while parsing scripts to prevent sending 'removes' before 'adds' #274
Conversation
I don't think this will fix your problem. You probably want a promise that you use as an async queue, like
|
The problem is that we are getting a Two options:
I think the first one is better for perf, and would hopefully prevent other issues with things that depend on the _scriptsById map. |
Could you add a test to the PR as well, that adds some scripts, refreshes the page, and ensures that the script load/unload events are sent in the proper order? You can take some inspiration from this failing test on how to do that. |
Ran into a bug with the newest typescript: microsoft/TypeScript#21491 |
b2b9611
to
4a5ce3a
Compare
src/chrome/chromeDebugAdapter.ts
Outdated
// Wait to finish loading sourcemaps from the initial scriptParsed events | ||
if (this._initialSourceMapsP) { | ||
const initialSourceMapsP = this._initialSourceMapsP; | ||
this._initialSourceMapsP = null; | ||
|
||
initialSourceMapsP.then(() => { | ||
await initialSourceMapsP.then(async () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The mixing of async/await and promises is a little confusing here (await ... .then(
)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
src/chrome/chromeDebugAdapter.ts
Outdated
this._session.sendEvent(new InitializedEvent()); | ||
this._earlyScripts.forEach(script => this.sendLoadedSourceEvent(script)); | ||
await Promise.all(this._earlyScripts.map(async script => await this.sendLoadedSourceEvent(script))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The async/await inside map
is redundant, just return the promise from this.sendLoadedSourceEvent
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
src/chrome/chromeDebugAdapter.ts
Outdated
this._earlyScripts = null; | ||
}); | ||
} | ||
} | ||
|
||
public doAfterProcessingSourceEvents(action: () => void): Promise<void> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
action
returns a Promise
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The use inside onExecutionContextsCleared() doesn't return a promise
src/chrome/chromeDebugAdapter.ts
Outdated
|
||
Promise.all(asyncOperations).then(() => | ||
this.clearTargetContext()); | ||
return Promise.all(asyncOperations).then(() => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is confusing, asyncOperations
will not be populated by this point because it's populated by an async function that we don't wait on. I'm not sure what's supposed to happen.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice catch... I missed this...
src/chrome/chromeDebugAdapter.ts
Outdated
this._session.sendEvent(scriptEvent))); | ||
}); | ||
private onExecutionContextsCleared(): Promise<void> { | ||
const asyncOperations = []; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this method picked up an extra level of indentation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
src/chrome/chromeDebugAdapter.ts
Outdated
}); | ||
|
||
Promise.all(asyncOperations).then(() => | ||
this.clearTargetContext()); | ||
return this.doAfterProcessingSourceEvents(async () => { // Execute after sending all the 'removed' events |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a confusing flow now. The asyncOperations array isn't needed if doAfterProcessingSourceEvents is forcing this to happen after sending the script removed events are sent.
Also, you could iterate the scripts inside the doAfterProcessingSourceEvent. That will be faster because the script processing doesn't have to happen one by one. You can use the await Promise.all(scriptById.map(async () => ...
pattern again.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I rewrote this to use async/wait. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is right. Async code is hard :) Thanks!
We were running into an issue where we send 'remove' myscript.js before we sent the 'add' myscript.js when we went into a page, and quickly redirected into another page.
That happened because we have some async code in how we process onScriptParsed, so that processing can be on-flight, and onExecutionContextsCleared very quickly sends the 'remove' events, and we weren't waiting for the onScriptParsed events to be completed.
This fixes solve part of the issue.
I think there is another unrelated reason that I'm investigating after this.