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

Migrating Async/Await to Promise #403

Merged
merged 35 commits into from
Jun 5, 2021
Merged

Conversation

byteab
Copy link
Contributor

@byteab byteab commented May 26, 2021

WIP

middleware.ts is completely migrated from Async/Await to Promise.
and also sync apis will be called in a sync manner.

  • a general API created to handle both sync/async peacefully
  • async tests passed
  • sync tests added
  • sync test passed
  • types added

fixing #346

@codesandbox-ci
Copy link

codesandbox-ci bot commented May 26, 2021

This pull request is automatically built and testable in CodeSandbox.

To see build info of the built libraries, click here or the icon next to each commit SHA.

Latest deployment of this branch, based on commit 473c16a:

Sandbox Source
React Configuration
React Typescript Configuration

src/middleware.ts Outdated Show resolved Hide resolved
src/middleware.ts Outdated Show resolved Hide resolved
src/middleware.ts Outdated Show resolved Hide resolved
Co-authored-by: Daishi Kato <dai-shi@users.noreply.github.com>
src/middleware.ts Outdated Show resolved Hide resolved
src/middleware.ts Outdated Show resolved Hide resolved
@barbogast
Copy link
Collaborator

@dai-shi https://gist.github.com/barbogast/5679eabc25b761da0f500f53ca3ea0c2 contains an idea for a fix of the remaining issue with the rehydration. Is this approach correct, or do you think it should be fixed in an entirely different way?

src/middleware.ts Outdated Show resolved Hide resolved
src/middleware.ts Outdated Show resolved Hide resolved
@barbogast
Copy link
Collaborator

Just updated both gists. The tests should be finished, and are passing against the updated middleware.ts gist. How do you want me to submit the code? A PR against https://github.com/TheEhsanSarshar/zustand/tree/async-await-fix? Or do you just want to grab the gists and add them to PR directly, @TheEhsanSarshar?

@barbogast
Copy link
Collaborator

I'm wondering... is it possible to correctly type makeThenable()? I had a quick try and didn't manage, especially since the returned object needs to be compatible with real Promises for TS (I guess?).

That together with the subtle but important differences between sync mode and async mode within the same code block make me wonder if it's worth it to handle both cases with the same code. Maybe it would be better to duplicate the code and handle both cases separately. Not sure, though, just sharing the thought.

src/middleware.ts Outdated Show resolved Hide resolved
@byteab
Copy link
Contributor Author

byteab commented May 28, 2021

Just updated both gists. The tests should be finished, and are passing against the updated middleware.ts gist. How do you want me to submit the code? A PR against https://github.com/TheEhsanSarshar/zustand/tree/async-await-fix? Or do you just want to grab the gists and add them to PR directly, @TheEhsanSarshar?

I am going to make some changes to add support for sync error handling. then you can make a PR on the repo

@byteab
Copy link
Contributor Author

byteab commented May 28, 2021

Hi dear @dai-shi.
in this gist, I just implemented a new toThenable that is able to handle errors in both syn/async. it also handles thrown exception inside then and catch
can you please have a look at that?
https://gist.github.com/TheEhsanSarshar/d9852a7cc102dae0f07186e1dc4fe700

@byteab
Copy link
Contributor Author

byteab commented May 28, 2021

the async tests are failing. dear @barbogast would you mind please check that.

@dai-shi
Copy link
Member

dai-shi commented May 28, 2021

https://gist.github.com/TheEhsanSarshar/d9852a7cc102dae0f07186e1dc4fe700#gistcomment-3761417
The conversations are scattered.
We could just paste the code here.

@barbogast
Copy link
Collaborator

FWIW, I created a PR with my results agains @TheEhsanSarshar 's branch: byteab#1. The changes are separated by commit, so you can cherry-pick. It contains the tests for the sync storage and code adjustments to middleware.ts so that all the tests are passing.

@barbogast
Copy link
Collaborator

in this gist, I just implemented a new toThenable that is able to handle errors in both syn/async. it also handles thrown exception inside then and catch
can you please have a look at that?
https://gist.github.com/TheEhsanSarshar/d9852a7cc102dae0f07186e1dc4fe700

Hmm... just realised, that it might be worth it to have separate tests toThenable, then. There are a lot of ways in which this function can be used, and without tests it might be tricky to guarantee that all of them are handled correctly. Especially thinking about all the different ways in which errors could occur.

@dai-shi
Copy link
Member

dai-shi commented May 29, 2021

Thanks guys for working on this.
I thought too that tests for toThenable would be nice.
But, we haven't agreed on the api yet.
Let me put my proposal here:

const toThenable = (fn) => (input) => {
  try {
    const result = fn(input)
    if (result instanceof Promise) {
      return result
    }
    return {
      then(onFulfilled) { return toThenable(onFulfilled)(result) },
      catch(onRejected) { return this },
    }
  } catch (e) {
    return {
      then(onFulfilled) { return this },
      catch(onRejected) { return toThenable(onRejected)(e) },
    }
  }
}

@byteab
Copy link
Contributor Author

byteab commented May 29, 2021

thanks @dai-shi I am going to playaround with your proposal

@byteab
Copy link
Contributor Author

byteab commented May 29, 2021

in this gist, I just implemented a new toThenable that is able to handle errors in both syn/async. it also handles thrown exception inside then and catch
can you please have a look at that?
https://gist.github.com/TheEhsanSarshar/d9852a7cc102dae0f07186e1dc4fe700

Hmm... just realised, that it might be worth it to have separate tests toThenable, then. There are a lot of ways in which this function can be used, and without tests it might be tricky to guarantee that all of them are handled correctly. Especially thinking about all the different ways in which errors could occur.

@barbogast are agree to finalize the api first and then write tests ?

@byteab byteab requested a review from dai-shi June 2, 2021 09:02
@barbogast
Copy link
Collaborator

barbogast commented Jun 2, 2021

Right now the code doesn't behave correctly if the version in localStorage doesn't match the version in the code and there is no migrate function. There is also no test covering this.

Is this case supposed to be handled correctly? Thinking about it, a console.error warning might also be helpful in this case.

--- edit
PR: byteab#4. Also, the code handling the migration can be simplified. Would do that later.

@byteab
Copy link
Contributor Author

byteab commented Jun 2, 2021

Right now the code doesn't behave correctly if the version in localStorage doesn't match the version in the code and there is no migrate function. There is also no test covering this.

Is this case supposed to be handled correctly? Thinking about it, a console.error warning might also be helpful in this case.

--- edit
PR: TheEhsanSarshar#4. Also, the code handling the migration can be simplified. Would do that later.

Yeah, as you said a single console.warn would be enough.

@byteab byteab requested a review from AnatoleLucet June 2, 2021 10:51
Co-authored-by: Anatole Lucet <anatole@hey.com>
Copy link
Member

@dai-shi dai-shi left a comment

Choose a reason for hiding this comment

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

Looks great, and getting close. There're a few things to fix for edge cases.
So sorry about the messy comments. If ambiguous, feel free to ask.

src/middleware.ts Outdated Show resolved Hide resolved
src/middleware.ts Outdated Show resolved Hide resolved
src/middleware.ts Outdated Show resolved Hide resolved
src/middleware.ts Show resolved Hide resolved
`Persist middleware: unable to update ${name}, the given storage is currently unavailable.`
)
}
})
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
})
}).catch((e) => { throw e })

I'm pretty sure we want this, no?

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 am not sure if it's working in a sync manner. let me check that

Copy link
Member

Choose a reason for hiding this comment

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

I think this magically works.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

👍

Copy link
Member

Choose a reason for hiding this comment

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

I meant it would work with my fix, which was wrong.
473c16a should be the fix.

src/middleware.ts Outdated Show resolved Hide resolved
barbogast and others added 2 commits June 3, 2021 11:08
Correctly handle version discrepancies with missing migrate functions
@byteab
Copy link
Contributor Author

byteab commented Jun 3, 2021

Here I have updated the gist.
https://gist.github.com/TheEhsanSarshar/d9852a7cc102dae0f07186e1dc4fe700
@dai-shi @AnatoleLucet @barbogast please have a look at this.

there are still typescript issues in line:287.
I think we should change toThenable types a little bit.

@dai-shi
Copy link
Member

dai-shi commented Jun 3, 2021

Here you go: playground

}
})
.then((deserializedStorageValue) => {
if (deserializedStorageValue) {
if (deserializedStorageValue.version !== version) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
if (deserializedStorageValue.version !== version) {
if ((deserializedStorageValue.version ?? false) && deserializedStorageValue.version !== version) {

see #409

Copy link
Member

Choose a reason for hiding this comment

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

removing ?? false just works?

Copy link
Collaborator

Choose a reason for hiding this comment

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

not if the version is 0

Copy link
Collaborator

Choose a reason for hiding this comment

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

No, my bad. This doesn't even work if the version is 0. So maybe something like: typeof deserializedStorageValue.version === "number" && deserializedStorageValue.version !== version?

Copy link
Member

Choose a reason for hiding this comment

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

StorageValue<S> requires version to be number so we can even throw an error if typeof ... !== "number".

Copy link
Collaborator

Choose a reason for hiding this comment

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

Well, that wouldn't permit to store a primitive value as in #409
But maybe there is a reason not to allow that?

Copy link
Member

Choose a reason for hiding this comment

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

well, if we want to allow non-version store, we should also change the StorageValue<S> type. I'm fine with either way.

Copy link
Member

Choose a reason for hiding this comment

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

Should we tackle it as a separate PR?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yes, let's merge this first

Copy link
Member

@dai-shi dai-shi left a comment

Choose a reason for hiding this comment

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

Looks great to me. Nice work!

@byteab
Copy link
Contributor Author

byteab commented Jun 5, 2021

Good job everybody.
Thank you @dai-shi @barbogast @AnatoleLucet for working on this.
It is an honor for me to work with such talented developers.

@barbogast
Copy link
Collaborator

Like-wise, after years working with open-source software I'm still amazed getting such high-quality software for free. And in addition to that it feels like there's 24/7 support with direct access to the core-developers, no premium-support-subscription needed.
But it's not one-sided since, as seen here, users are happy to contribute and collaborate, and so it's a common effort, rather than provider-consumer. I feel that the open source development is one of the best and highest value-generating way of collaboration that humans have.

Hehe, got all philosophical here 🙈 Anyway, thanks all 😍

Copy link
Collaborator

@AnatoleLucet AnatoleLucet left a comment

Choose a reason for hiding this comment

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

Lgtm. Many thanks to everyone for your hard work! 🚀

@dai-shi dai-shi merged commit f62d448 into pmndrs:master Jun 5, 2021
@byteab byteab deleted the async-await-fix branch June 5, 2021 10:39
@barbogast barbogast mentioned this pull request Jun 6, 2021
16 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants