Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace when.js with native promises #10149

Merged
merged 1 commit into from
Mar 16, 2022
Merged

Replace when.js with native promises #10149

merged 1 commit into from
Mar 16, 2022

Conversation

ggetz
Copy link
Contributor

@ggetz ggetz commented Feb 28, 2022

Replaces #8525
Fixes #3967
Fixes #9578

This PR removes when.js in favor of native promises. Besides removing the files related to when itself, this generally follows the migration guide as laid out in the migration guide, swapping out when.defer with a new defer function, switching otherwise for catch, and always for finally.

There were a few areas in Source where a function was assumed to be executed synchronously when a function was resolved. Native Promises however, by spec, will resolve at the end of a frame. There were also some strange handling of promise rejection in imagery providers that I cleaned up a bit in order to make them testable.

  • In LabelCollection and EntityCluster, order of execution adjustment where made.
  • Added TextureAtlas.addImageSync to support the order of execution fix in LabelCollection
  • ArcGisMapServerImageryProvider.readyPromise will not reject if there is a failure unless the request cannot be retried.
  • SingleTileImageryProvider.readyPromise will not reject if there is a failure unless the request cannot be retried.

The majority of the changes lie in the Specs, where unresolved promises weren’t being awaited before finishing executions, and where resolved promises are assumed to be synchronous all over the place and needed a good amount of fixes. Another issue which came up was calling Promise.reject in the body of a spec can cause node to halt execution when running via the command line.

Finally, I did need to tweak eslint in order to turn off the rule which disallows promises.

Opening this a bit early. Still to do:

  • Merge master
  • Fix remaining spec failures, travis-only failures
  • Open issues for excluded specs, and for refactoring out use of defer in general
  • Add documentation for defer
  • Update CHANGES.md - Add note about ArcGisImageServerProvider rejecting ready promise if there is a failure and retry is false.
  • Check build-ts warnings

@cesium-concierge
Copy link

Thanks for the pull request @ggetz!

  • ✔️ Signed CLA found.
  • CHANGES.md was not updated.
    • If this change updates the public API in any way, please add a bullet point to CHANGES.md.
  • ❔ Unit tests were not updated.
    • Make sure you've updated tests to reflect your changes, added tests for any new code, and ran the code coverage tool.

Reviewers, don't forget to make sure that:

  • Cesium Viewer works.
  • Works in 2D/CV.

@ggetz ggetz mentioned this pull request Feb 28, 2022
6 tasks
@chris-cooper
Copy link
Contributor

chris-cooper commented Mar 2, 2022

It will be great to see this at last! Did you consider a major version update due to the API change @ggetz ? https://semver.org/

@ggetz ggetz self-assigned this Mar 2, 2022
@ggetz
Copy link
Contributor Author

ggetz commented Mar 3, 2022

Thanks @chris-cooper, historically cesiumjs has chosen not to use semver due to frequent breaking changes especially early in development. However, we are presently considering moving to semver to make updates like this easier. We're in the process of confirming this decision since it will likely have impacts among government users. So we're thinking about it, but I can't promise a version increment for this next release.

@ggetz
Copy link
Contributor Author

ggetz commented Mar 4, 2022

I know @kring mentioned that defer is not the preferred way to go, and to use the Promise constructor instead as its better practice for handling errors. I think for the sake of limiting the scope of this PR, we leave it in. However, I'm assuming we should at least add a warning to the defer docs explaining that's its preferable to use Promise directly instead?

@mramato
Copy link
Contributor

mramato commented Mar 4, 2022

I'm assuming we should at least add a warning to the defer docs explaining that's its preferable to use Promise directly instead?

This is private, right? So I assume you mean the internal documentation?

I agree with @kring that all of Cesium's defer usage would be better served via new Promise. I'm also okay with making that a second pass. It will be a lot easier to do than the effort this took, either way. We should avoid adding any new defer calls in future code.

@ggetz
Copy link
Contributor Author

ggetz commented Mar 4, 2022

@mramato when.defer was part of the public API, so I assumed we should make defer public as well, at least for this initial change. If you recommend making a clean break, I can update the migration guide and suggest any usage of when.defer should instead be replaced with a new Promise.

@mramato
Copy link
Contributor

mramato commented Mar 4, 2022

@ggetz I would definitely recommend a clean break. Anyone that was using defer is going to have to update their code regardless.

@ggetz
Copy link
Contributor Author

ggetz commented Mar 4, 2022

The docs, CHANGES.md, and the migration guide on the forum have been updated accordingly.

I'm still fixes a handful of specs, but this is almost complete and should be ready for review. @sanjeetsuhag Could you please do a pass on this?

@sanjeetsuhag
Copy link
Contributor

@ggetz I went through all the Sandcastles and it seems like the only problematic one is CZML Custom Properties.

@ggetz
Copy link
Contributor Author

ggetz commented Mar 8, 2022

Thanks @sanjeetsuhag, fixed! That test just needed to await loading the CZML.

The tests are also in order now. I had to exclude a handful, mostly 3D tiles related, specs as there was some questionable error handling that led to errors being thrown that cannot be sensibly caught. I'm opening an issue for these excluded tests and will link it back into the specs.

Specs/Scene/ModelSpec.js Outdated Show resolved Hide resolved
@ggetz
Copy link
Contributor Author

ggetz commented Mar 10, 2022

I wasn't able to duplicate locally, but I believed I've handled the intermittent failure that was happening in CI. @sanjeetsuhag do you have any additional feedback?

@sanjeetsuhag
Copy link
Contributor

@ggetz I looked at the latest commits, and I have no comments on the code changes. I can only speak to the excluded ModelExperimental specs and that #10178 should be a high priority fix before the release: lots of important tests in there.

@ggetz
Copy link
Contributor Author

ggetz commented Mar 10, 2022

Thanks @sanjeetsuhag. I do agree that its high priority, and labeled it accordingly. There's also only two tests in ModelExperimentalSpec which have been excluded because of the error handling, not the whole suite.

@mramato
Copy link
Contributor

mramato commented Mar 10, 2022

Have we ran any performance benchmarks against main to ensure that we haven't introduced a perf regression in any of the most common areas? (Like CWT + 3DTiles loading) or simple.czml animating on an empty globe.

Not sure if we have any "canned" perf tests ready to go, perhaps @lilleyse might have some ideas about what we have done for 3D Tiles testing.

if (!defined(this._backgroundTextureAtlas)) {
this._backgroundTextureAtlas = new TextureAtlas({
context: context,
initialSize: whitePixelSize,
});
backgroundBillboardCollection.textureAtlas = this._backgroundTextureAtlas;
addWhitePixelCanvas(this._backgroundTextureAtlas, this);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks like a bugfix in addition to Promise update? Is that true? If so, what bug does it fix and should we include that in CHANGES? (even if we have no issue number for it).

I noticed a few places where there are little changes like this that appear to be bug fixes of some kind. If we just figured out the code is wrong but don't know how/if a bug manifested from it, that's fine too, nothing to report. But I just wanted to point it out in case.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These were to address cases where the code assumed a promise was executing synchronously. When transitioning to native promises, even in cases where the promise immediately resolved, it wouldn't do so until the end of the frame. So the timing of the rest of the function is thrown off. I don't think these fix any existing bugs, just keep behavior working as before.

@@ -353,6 +352,49 @@ function addImage(textureAtlas, image, index) {
textureAtlas._guid = createGuid();
}

function getIndex(atlas, image) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a general rule, stuff like this should be called out in the PR description. I assume by looking at it we had to add some branch new code in order to provide a path for synchronous operations that would otherwise be async in our new "native" Promise world, but in a PR this big; it would be easy for a reviewer to miss. Also, if someone needs to use revision history to track where/why this code was added, it won't be obvious to them by reading the PR description (which it should be).

Even just a single bullet like "Added TextureAtlas.addImageSync to xxxx" would be extremely valuable to future devs.

This is especially true given the terseness of many of the commit messages in this PR (or perhaps we are squashing) but either way, a paper trail is important.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I planned on squashing commits with a more useful commit message. But understood, I'll call out these changes in the PR explicitly.

@mramato
Copy link
Contributor

mramato commented Mar 11, 2022

Two big things that jumped out at me:

  • Holy smokes do we abuse defer!
  • Moving to async/await (Which can happen organically/gradually) would be a huge improvement for readability and verbosity for our code base.

@mramato
Copy link
Contributor

mramato commented Mar 11, 2022

Just noticed this:

Screenshot_20220310_192919

Is that because of test fixes or something else? (This is more out of curiosity than anything else).

@mramato
Copy link
Contributor

mramato commented Mar 11, 2022

Okay @ggetz I'll admit I kind of lost steam when I got to the specs, so I didn't review them thoroughly by any means. But this PR looks great and those minor comments were all I had. This is a major stepping stone for CesiumJS and an awesome way to start a true modernization effort.

@ggetz
Copy link
Contributor Author

ggetz commented Mar 14, 2022

Is that because of test fixes or something else?

Yes, the extra code is definitely coming from spec modifications. There were tons of cases where we needed to add a few lines to await a promise before continuing.

@ggetz
Copy link
Contributor Author

ggetz commented Mar 14, 2022

Have we ran any performance benchmarks against main to ensure that we haven't introduced a perf regression in any of the most common areas? (Like CWT + 3DTiles loading) or simple.czml animating on an empty globe.

I've run several 3D Tiles and CWT examples including various photogrammetry, point clouds, and OSM buildings. I'm seeing similar loading times using the Chome profiler on main and this branch. Likewise with simple.czml I'm seeing comparable framerates. @lilleyse If we have more exact tests for performance here, please let me know and I'd be happy to compare.

@lilleyse
Copy link
Contributor

I usually just record how long it takes for tiles to load for a given view. Sometimes I'll also set up a flight path. Here are two of my go-to sandcastles:

Globe 3D Tiles
terrainSandcastle 3dtilesSandcastle

@ggetz
Copy link
Contributor Author

ggetz commented Mar 15, 2022

Awesome thanks @lilleyse!

I'm seeing a comparative range of loading times with both this branch and main for both the terrain and tileset examples.

And thanks @mramato for all the feedback here! I believe I've addressed everything at this point.

Is there any remaining feedback or is this ready?

There were a few areas in Source where a function was assumed to be executed synchronously when a function was resolved. Native Promises however, by spec, will resolve at the end of a frame. There were also some strange handling of promise rejection in imagery providers that I cleaned up a bit in order to make them testable.

- In LabelCollection and EntityCluster, order of execution adjustment where made.
- Added TextureAtlas.addImageSync to support the order of execution fix in LabelCollection
- ArcGisMapServerImageryProvider.readyPromise will not reject if there is a failure unless the request cannot be retried.
- SingleTileImageryProvider.readyPromise will not reject if there is a failure unless the request cannot be retried.

The majority of the changes lie in the Specs, where unresolved promises weren’t being awaited before finishing executions, and where resolved promises are assumed to be synchronous all over the place and needed a good amount of fixes. Another issue which came up was calling Promise.reject in the body of a spec can cause node to halt execution when running via the command line.
@mramato
Copy link
Contributor

mramato commented Mar 16, 2022

200

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
No open projects
Status: Issue/PR closed
Development

Successfully merging this pull request may close these issues.

computeIcrfToFixedMatrix test suite has intermittent failures Upgrade when.js to native promises
6 participants