-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
feat(context): forbid bind().to() a Promise instance #854
Conversation
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 think this is an important point, and a good change.
Promises are only ever meant to resolve once, so binding them on .to
is the same sort of anti-pattern as creating .on(...)
event handlers that resolve promises.
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.
Does this have any impact on components which return a value()
function that resolves a promise? (I think log-example-extension
might have an example of that or even authentication
.
@@ -253,15 +253,15 @@ describe('Context', () => { | |||
|
|||
describe('get', () => { | |||
it('returns a promise when the binding is async', async () => { | |||
ctx.bind('foo').to(Promise.resolve('bar')); | |||
ctx.bind('foo').toDynamicValue(() => Promise.resolve('bar')); |
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.
This can be further simplified as ctx.bind('foo').toDynamicValue(async () => 'bar');
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.
Agreed.
I personally prefer Promise.resolve
in this particular case, because async
functions incur overhead in terms of the generated code (either explicitly by tsc
for Node.js 6.x or implicitly by V8).
We can use
Please also add a test to reject |
Please rebase it against latest master to get CI passing. |
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.
LGTM a great practice to learn Promise and event loop.
I agree that the value should be resolved when we retrieve it by a ctx key, not when we bind the ctx key to it.
Hmm..a question(maybe related) when looking at the context functions:
Why we don't allow people to get a Promise from the context but always a value that is resolved? I find getValueOrContext
but it looks like only for internal use.
Promises are a construct primarily intended for flow control: In an algorithm with steps 1 and 2, we want to wait for the outcome of step 1 before starting step 2. Promises are NOT a tool for storing values that may become available in the future, depending on the success or a failure of a background async task. Values stored in bindings are typically accessed only later, in a different turn of the event loop or the Promise micro-queue. As a result, when a promise is stored via `.to()` and is rejected later, then more likely than not, there will be no error (catch) handler registered yet, and Node.js will print "Unhandled Rejection Warning". BREAKING CHANGE: It is no longer possible to pass a promise instance to `.to()` method of a Binding. Use `.toDynamicValue()` instead. Consider deferring the async computation (that produced the promise instance you are binding) into the dynamic value getter function, i.e. start the async computation only from the getter function. An example diff showing how to upgrade your existing code: - ctx.bind('bar').to(Promise.resolve('BAR')); + ctx.bind('bar').toDynamicValue(() => Promise.resolve('BAR'));
74a3c24
to
d7415e8
Compare
@jannyHou Are you referring to
When the users are sure the value can be obtained synchronously, they can use I think you raised a valid question, would you mind updating our documentation (e.g. Key Concepts >> Context) so that the next person wondering about this topic can find the answer already written down? |
The Travis build has failed on Commit linting:
I am going to ignore this error and send a new pull request to tweak commit linter config. |
Thanks @bajtos I will create a PR for the document. |
Promises are a construct primarily intended for flow control: In an algorithm with steps 1 and 2, we want to wait for the outcome of step 1 before starting step 2.
Promises are NOT a tool for storing values that may become available in the future, depending on the success or a failure of a background async task.
Values stored in bindings are typically accessed only later, in a different turn of the event loop or the Promise micro-queue. As a result, when a promise is stored via
.to()
and is rejected later, then more likely than not, there will be no error (catch) handler registered yet, and Node.js will print "Unhandled Rejection Warning".BREAKING CHANGE
It is no longer possible to pass a promise instance to
.to()
method of a Binding. Use.toDynamicValue()
instead. Consider deferring the async computation (that produced the promise instance you are binding) into the dynamic value getter function, i.e. start the async computation only from the getter function.An example diff showing how to upgrade your existing code:
This is a follow-up for our discussion in #671, in particular #671 (comment)
Checklist
npm test
passes on your machinepackages/cli
were updatedpackages/example-*
were updated