Skip to content

Conversation

@raymondfeng
Copy link
Contributor

@raymondfeng raymondfeng commented May 4, 2018

This PR is spin-off from #983. It extracts a few utilities to help handle ValueOrPromise.

Checklist

  • npm test passes on your machine
  • New tests added or existing tests modified to cover all changes
  • Code conforms with the style guide
  • API Documentation in code was updated
  • Documentation in /docs/site was updated
  • Affected artifact templates in packages/cli were updated
  • Affected example projects in examples/* were updated

@raymondfeng raymondfeng requested a review from bajtos as a code owner May 4, 2018 17:08
return Object.assign(inst, propertiesOrPromise);
return new ctor(...args);
});
return resolveValueOrPromise(propertiesOrPromise, props => {
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 as to why we don't use a promise style here?

Copy link
Contributor Author

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);
Copy link
Member

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 resolveUntil to always work asynchronously - ensure the recursive call of resolveUntil is always deferred to the next tick of event loop or promise micro-queue. This will allow us to use resolveValueOrPromise.
  • 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>(
Copy link
Member

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.

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 choose resolveUntil to be consistent with other methods such as resolveList.

Copy link
Member

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>(
Copy link
Member

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?

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 renamed it to transformValueOrPromise

expect(result).to.eql('A');
});

it('handles a rejected promise', () => {
Copy link
Member

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);});

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added

@raymondfeng raymondfeng force-pushed the resolve-value-or-promise branch from f92f81b to dd119f8 Compare May 9, 2018 15:46
@raymondfeng
Copy link
Contributor Author

I believe this will cause stack overflow error when all values are resolved synchronously and the Iterator returns enough items to overflow the stack.

Good catch. I rewrote the implementation to avoid that. A few tests are added to cover the corner case.

@raymondfeng
Copy link
Contributor Author

@bajtos PTAL

@raymondfeng raymondfeng force-pushed the resolve-value-or-promise branch from dd119f8 to b07954c Compare May 9, 2018 16:04
* @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>(
Copy link
Member

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)) {
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 allow evaluator to return a Promise<boolean> too?

Copy link
Contributor Author

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.

@raymondfeng raymondfeng merged commit cc55ef5 into master May 9, 2018
@raymondfeng raymondfeng deleted the resolve-value-or-promise branch May 9, 2018 16:12
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