-
Notifications
You must be signed in to change notification settings - Fork 1.1k
feat(context): add more utils to resolve valueOrPromises #1303
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
Conversation
packages/context/src/resolver.ts
Outdated
| return Object.assign(inst, propertiesOrPromise); | ||
| return new ctor(...args); | ||
| }); | ||
| return resolveValueOrPromise(propertiesOrPromise, props => { |
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.
is there a reason as to why we don't use a promise style here?
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.
We need to do the work synchronously or asynchronously based on the return value. The usage of ValueOrPromise to allow us to call context.getSync() and context.get().
| const valueOrPromise = resolver(sourceVal); | ||
| return resolveValueOrPromise(valueOrPromise, v => { | ||
| if (evaluator(sourceVal, v)) return v; | ||
| else return resolveUntil(source, resolver, evaluator); |
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 believe this will cause stack overflow error when all values are resolved synchronously and the Iterator returns enough items to overflow the stack.
I am not sure if there is any elegant way how to keep supporting both sync/async and using resolveValueOrPromise under the hood. I see two ways forward that should be relatively straightforward.
- Modify
resolveUntilto always work asynchronously - ensure the recursive call ofresolveUntilis always deferred to the next tick of event loop or promise micro-queue. This will allow us to useresolveValueOrPromise. - Rework the code to iterate inside the same function, drop
resolveValueOrPromise. This will preserve sync/async semantics.
Before you start working on this, please add a test that's triggering stack overflow error - this will verify the assumptions and serve as a proof that the new version works correctly.
| * @param resolver The resolve function that maps the source value to a result | ||
| * @param evaluator The evaluate function that decides when to stop | ||
| */ | ||
| export function resolveUntil<T, V>( |
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 find the name resolveUntil confusing. In my mind, we resolve values from a context (IoC container), but there is no context involved here. I am proposed to rename this method to mapUntil.
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 choose resolveUntil to be consistent with other methods such as resolveList.
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.
Fair enough 👍
| * @param valueOrPromise The value or promise | ||
| * @param resolver A function that maps the source value to a value or promise | ||
| */ | ||
| export function resolveValueOrPromise<T, V>( |
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 find the name resolveValueOrPromise confusing. In my mind, we resolve values from a context (IoC container), but there is no context involved here. I am proposed to rename this function to mapValueOrPromise or perhaps transformValueOrPromise.
OTOH, it is true that Promises can be resolved too, so maybe "resolve" is no that bad.
@strongloop/sq-lb-apex Thoughts?
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 renamed it to transformValueOrPromise
| expect(result).to.eql('A'); | ||
| }); | ||
|
|
||
| it('handles a rejected 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.
Please add another test to verify how sync errors are handled.
resolveValueOrPromise('a', v => {throw new Error(v);});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.
Added
f92f81b to
dd119f8
Compare
Good catch. I rewrote the implementation to avoid that. A few tests are added to cover the corner case. |
|
@bajtos PTAL |
dd119f8 to
b07954c
Compare
| * @param resolver The resolve function that maps the source value to a result | ||
| * @param evaluator The evaluate function that decides when to stop | ||
| */ | ||
| export function resolveUntil<T, V>( |
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.
Fair enough 👍
| const valueOrPromise = resolver(sourceVal); | ||
| if (isPromiseLike(valueOrPromise)) { | ||
| return valueOrPromise.then(v => { | ||
| if (evaluator(sourceVal, v)) { |
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.
Should we allow evaluator to return a Promise<boolean> too?
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.
We can add it if a use case comes up.
This PR is spin-off from #983. It extracts a few utilities to help handle ValueOrPromise.
Checklist
npm testpasses on your machinepackages/cliwere updatedexamples/*were updated