Skip to content

Enable no-floating-promises lint check and fix / suppress violations. #1087

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

Merged
merged 2 commits into from
Aug 3, 2018

Conversation

mikelehen
Copy link
Contributor

I noticed that we were failing to wait for promises in a couple places so I enabled no-floating-promises lint check and cleaned up all the violations. To make this a bit easier I introduced enqueue() vs enqueueAndWait() on the AsyncQueue, with the former returning void instead of a Promise.

/**
* Adds a new operation to the queue. Returns a promise that will be resolved
* when the promise returned by the new operation is (with its value).
*/
enqueue<T extends Unknown>(op: () => Promise<T>): Promise<T> {
enqueueAndWait<T extends Unknown>(op: () => Promise<T>): Promise<T> {
Copy link
Contributor

Choose a reason for hiding this comment

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

The common case (and thus most likely to have a parallel on the other platforms) would be to enqueue without dropping the promise, no?

If so I'd prefer we continue to name this enqueue and call the somewhat danergous enqueue and drop promise something else.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Actually there is no enqueueAndWait() on the other platforms (dispatchAsync on iOS and enqueue on Android both return void and don't block). Instead you have to monkey with completions / TaskCompletionSource to plumb the async operation result back to the caller.

enableNetwork on web:

  enableNetwork(): Promise<void> {
    return this.asyncQueue.enqueueAndWait(() => {
      return this.remoteStore.enableNetwork();
    });
  }

enableNetwork on iOS:

- (void)enableNetworkWithCompletion:(nullable FSTVoidErrorBlock)completion {
  [self.workerDispatchQueue dispatchAsync:^{
    [self.remoteStore enableNetwork];
    if (completion) {
      self->_userExecutor->Execute([=] { completion(nil); });
    }
  }];
}

enableNetwork on Android:

  public Task<Void> enableNetwork() {
    final TaskCompletionSource<Void> source = new TaskCompletionSource<>();
    asyncQueue.enqueue(
        () -> {
          remoteStore.enableNetwork();
          source.setResult(null);
        });
    return source.getTask();
  }

So I think enqueue() is arguably the common case and is named consistently with the other platforms.

Also, I can't think of a good name if I make enqueue() the "blocking" one. enqueueAndForget()?

Copy link
Contributor

Choose a reason for hiding this comment

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

I guess I consider enqueue-that-returns-a-promise on web to be equivalent to enqueue-that-plumbs-through-callbacks on iOS and equivalent to enqueue-with-a-manually-wrapping TaskCompletionSource on Android.

We could have Android's enqueue do the TaskCompletionSource monkeying as well (TaskCompletionSource being the equivalent of a Promise), which would bring it further into line with web.

In any case, I think we generally want to tie actions enqueued with a user-visible promise so this should be be the shorter, more obvious name.

Regarding naming: enqueueAndWait suggests actually waiting, which isn't what's happening. Meanwhile enqueueAndForget seems to reasonably suggest what it's doing to me.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fair enough. Renamed!

I'll take a look at the Android change.

Copy link
Contributor

@wilhuff wilhuff left a comment

Choose a reason for hiding this comment

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

LGTM

@@ -435,7 +435,9 @@ export class FirestoreClient {

write(mutations: Mutation[]): Promise<void> {
const deferred = new Deferred<void>();
this.asyncQueue.enqueue(() => this.syncEngine.write(mutations, deferred));
this.asyncQueue.enqueueAndForget(() =>
this.syncEngine.write(mutations, deferred)
Copy link
Contributor

Choose a reason for hiding this comment

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

Could syncEngine hide this Deferred?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not quite. The spec tests need to be able to wait for the write() to complete locally without waiting for the user callback (after the server ack of the write). So there are two separate async things you might want to wait on.

@@ -396,6 +396,7 @@ export class IndexedDbPersistence implements Persistence {

// Attempt graceful shutdown (including releasing our owner lease), but
// there's no guarantee it will complete.
// tslint:disable-next-line:no-floating-promises
this.shutdown();
Copy link
Contributor

Choose a reason for hiding this comment

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

This runs on the queue in the multi-tab branch. Should we change it here to also use enqueueAndForget?

This code is somewhat untested and I am a little afraid of its raciness if we don't enqueue it.

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 tried but we don't have access to the async queue here right now. Maybe you should get around to merging multi-tab. 😛

new FirestoreError(Code.UNAUTHENTICATED, '')
);
})
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there a reason you added the extra .then() instead of using await?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes. If I changed this then() callback to async/await then I'd feel compelled to convert the whole test, and then I'd feel compelled to convert the whole file, and at that point I may as well go ahead and convert the rest of our codebase. At that point I'd be so good at it I might as well start tackling other open source projects. And then I'd never get anything done.

Copy link
Contributor Author

@mikelehen mikelehen left a comment

Choose a reason for hiding this comment

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

I'm disinclined to acquiesce to your requests. Thanks anyway!

@@ -435,7 +435,9 @@ export class FirestoreClient {

write(mutations: Mutation[]): Promise<void> {
const deferred = new Deferred<void>();
this.asyncQueue.enqueue(() => this.syncEngine.write(mutations, deferred));
this.asyncQueue.enqueueAndForget(() =>
this.syncEngine.write(mutations, deferred)
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not quite. The spec tests need to be able to wait for the write() to complete locally without waiting for the user callback (after the server ack of the write). So there are two separate async things you might want to wait on.

@@ -396,6 +396,7 @@ export class IndexedDbPersistence implements Persistence {

// Attempt graceful shutdown (including releasing our owner lease), but
// there's no guarantee it will complete.
// tslint:disable-next-line:no-floating-promises
this.shutdown();
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 tried but we don't have access to the async queue here right now. Maybe you should get around to merging multi-tab. 😛

new FirestoreError(Code.UNAUTHENTICATED, '')
);
})
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes. If I changed this then() callback to async/await then I'd feel compelled to convert the whole test, and then I'd feel compelled to convert the whole file, and at that point I may as well go ahead and convert the rest of our codebase. At that point I'd be so good at it I might as well start tackling other open source projects. And then I'd never get anything done.

@mikelehen mikelehen merged commit dc9a5d1 into master Aug 3, 2018
@mikelehen mikelehen deleted the mikelehen/no-floating-promises branch August 3, 2018 20:37
@firebase firebase locked and limited conversation to collaborators Oct 17, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants