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

doc: more use-cases for promise events #3438

Closed
wants to merge 1 commit into from

Conversation

domenic
Copy link
Contributor

@domenic domenic commented Oct 19, 2015

This adds an example of a semi-common promise pattern that could
result in false-positive 'unhandledRejection' events, to help motivate
why there is a dual 'rejectionHandled' event. I anticipate it being
helpful to users who encounter 'unhandledRejection' events that do not
stem from obvious typos such as the JSON.pasre example.

Also cleans up the promise rejection tracking sample. By using a Set
instead of array we can get O(1) deletion. And, fixed indentation and
backtick usage there to align with the rest of the document.


Supercedes #979. Hopefully things can be more civil this time.

@domenic domenic added the doc Issues and PRs related to the documentations. label Oct 19, 2015
@benjamingr
Copy link
Member

Please consider the discussion in #979 before merging.

If we warn about the two issues in the PR (that the Set can leak, and that you still have false positives) I'm more OK with it (although I definitely think we should wait for @petkaantonov to weigh in here).

and #979 (comment)

@benjamingr
Copy link
Member

Basically, summing up the pending issues I have with it:

  • It should warn about the solution not working for all false positives and that there are still false positives (it is an inherent problem with a temporal construct that changes states).
  • It should warn about the potential memory leak.
  • The constructor example should probably be clarified (as @Fishrock123 noted). As in the discussion doc: more use-cases for promise events #979 (comment) .

When these gets addressed I'm all for merging.


function SomeResource() {
// Initially set the loaded status to a rejected promise
this.loaded = Promise.reject(new Error('Resource not yet loaded!'));
Copy link
Contributor

Choose a reason for hiding this comment

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

This patten is confusing to me. How does data actually get there once loaded?

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 have not shown a complete implementation of SomeResource. I could do so if it would help. Essentially there's a SomeResource.prototype.load() method that will replace the promise with a successful one.

The idea is that anyone doing .loaded.then(...) before anything is loaded goes down the error path. Whereas, after something is loaded, they go down the success path.

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 fleshed this out in full painful detail https://gist.github.com/domenic/854f0dc0f698a63b0e8a. I think including all that would be way too much for the docs, but if there's a way to make it clearer, I'm happy to do so.

Copy link
Contributor

Choose a reason for hiding this comment

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

That makes sense.

@Qard
Copy link
Member

Qard commented Oct 19, 2015

I'm fine with merging this, if it better highlights the potentially leaky aspect of the unhandledRejections set and the fact that, at any given time, not all items in unhandledRejections are necessarily in a state that they would never be handled.

As for my comment on making it a topic doc, I don't think that should be a blocker, but should be something we think about in the future. As yet, the non-reference docs situation is not yet entirely nailed down.

});

You could then record this list in some error log, either periodically or upon
process exit. This would avoid the false positive seen in the above resource
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe better: "This would then only expose the developer errors as shown above in'unhandledRejection' example."

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure, since some people are allergic to the term false positive that seems like a good way to defuse the situation.

Copy link
Contributor

Choose a reason for hiding this comment

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

You could then record this list in some error log, either periodically or upon process exit. This would avoid the false positive

This doesn't avoid the false positive. It makes a false positive significantly less likely. I don't think those are the same thing, but if there is otherwise consensus then I won't object 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.

Used @Fishrock123's phrasing

@domenic
Copy link
Contributor Author

domenic commented Oct 19, 2015

I'm fine with merging this, if it better highlights the potentially leaky aspect of the unhandledRejections set

Can you suggest some phrasing? All I can think of is "since the unhandledRejection set lives for the lifetime of your program, it will consume memory for the lifetime of your program, the same as every other object that lives for the lifetime of your program."

@domenic
Copy link
Contributor Author

domenic commented Oct 19, 2015

the fact that, at any given time, not all items in unhandledRejections are necessarily in a state that they would never be handled.

I tried to cover this in the existing text with "unlike in synchronous code where there is
an ever-growing list of unhandled exceptions, with promises there is a
growing-and-shrinking list of unhandled rejections"

@petkaantonov
Copy link
Contributor

Can you suggest some phrasing? All I can think of is "since the unhandledRejection set lives for the lifetime of your program, it will consume memory for the lifetime of your program, the same as every other object that lives for the lifetime of your program."

I don't think it's very good to make an example that is essentially a memory leak and then basically say "btw this obviously leaks memory, figure out some way to avoid that". There is just no way to phrase that well. The example should be fixed so that there is no inherent memory leak to begin with e.g. some interval that periodically clears the list or such.

});

You could then record this list in some error log, either periodically or upon
process exit. This would then only expose the developer errors as shown above
in the `'unhandledRejection'` example.
Copy link
Contributor

Choose a reason for hiding this comment

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

Perhaps it is better to reword and put this into the 'unhandledRejection' part, it's missing context here.

"below is an example ..."

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 rephrased it, hopefully in a way that addresses @petkaantonov's concerns as well.

@Fishrock123
Copy link
Contributor

I'm not 100% sure about the entire pre-populate error-promise as a notification thing, but the wording of the rest of the docs now appears sound. I'll let others chime in.

// no .catch or .then on resource.loaded for at least a turn

To deal with cases like this, which you may not want to track as developer
error in the same way as other `'unhandledRejection'` events, you can either
Copy link
Contributor

Choose a reason for hiding this comment

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

"which you may not want to track as developer error in the same way as other 'unhandledRejection' events"

Sentence structure is a little weird here, also "errors".

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Split into two sentences and rephrased a bit.

@Qard
Copy link
Member

Qard commented Oct 19, 2015

@domenic I think the wording needs to make more reference to the content of the set, not the existence of the set. It will continue to be a single set through the life of the program, but the content could grow over time. In an app that is not sufficiently handling promises, it could grow to the point of consuming all memory and terminating the app.

I'm not sure I agree exactly with @petkaantonov on the need for code to clear the list, just due to the verbosity of that, but at least explaining that real code would need to do that is important, I think.

@mscdex mscdex added the process Issues and PRs related to the process subsystem. label Oct 19, 2015
@domenic
Copy link
Contributor Author

domenic commented Oct 19, 2015

@Qard at the cost of some extra verbosity, expanded the last paragraph along those lines.

@Qard
Copy link
Member

Qard commented Oct 19, 2015

LGTM

@petkaantonov
Copy link
Contributor

lgtm

@benjamingr
Copy link
Member

This is much better - what about the constructor example? I'd be happy if we changed that a bit.

This adds an example of a semi-common promise pattern that could
result in false-positive 'unhandledRejection' events, to help motivate
why there is a dual 'rejectionHandled' event. I anticipate it being
helpful to users who encounter 'unhandledRejection' events that do not
stem from obvious typos such as the JSON.pasre example.

Also cleans up the promise rejection tracking sample. By using a Map
instead of array we can get O(1) deletion and actually record the errors.
And, fixed indentation and backtick usage there to align with the rest of
the document.
@benjamingr
Copy link
Member

I saw an interesting use case today, what about something like the following?

    var promises = [];
    myEventEmitter.on("data", url => {
          promises.push(makeRequest(url));
    }).on("done", () => Promise.all(promises).then(results => {
           // do work
    });

Without suppressing the error makeRequest might cause - this will cause an unhandled rejection most likely, and creating promises inside an event emitter seems pretty common. I think it might be a good alternative to the current example with the constructor.

Again, other than that LGTM.

@domenic
Copy link
Contributor Author

domenic commented Oct 20, 2015

This has gotten LGTMs from five collaborators now (1, 2, 3, 4, 5). There is one vocal non-collaborator who wants to make further changes, but I respectfully ask that we merge this now and @benjamingr can make any further changes as pull requests.

@benjamingr
Copy link
Member

To clarify - as I said in my last two comments - it looks good to me, further improvements can go to a new PRs.

The tone I do not appreciate so much but I can live with it :)

@jasnell
Copy link
Member

jasnell commented Oct 21, 2015

LGTM. Will land.

domenic added a commit that referenced this pull request Oct 21, 2015
This adds an example of a semi-common promise pattern that could
result in false-positive 'unhandledRejection' events, to help motivate
why there is a dual 'rejectionHandled' event. I anticipate it being
helpful to users who encounter 'unhandledRejection' events that do not
stem from obvious typos such as the JSON.pasre example.

Also cleans up the promise rejection tracking sample. By using a Map
instead of array we can get O(1) deletion and actually record the errors.
And, fixed indentation and backtick usage there to align with the rest of
the document.

Reviewed-By: Stephan Belanger <admin@stephenbelanger.com>
Reviewed-By: Petka Antonov <petka.antonov@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
PR-URL: #3438
@jasnell
Copy link
Member

jasnell commented Oct 21, 2015

Landed in d381290

@jasnell jasnell closed this Oct 21, 2015
domenic added a commit that referenced this pull request Oct 22, 2015
This adds an example of a semi-common promise pattern that could
result in false-positive 'unhandledRejection' events, to help motivate
why there is a dual 'rejectionHandled' event. I anticipate it being
helpful to users who encounter 'unhandledRejection' events that do not
stem from obvious typos such as the JSON.pasre example.

Also cleans up the promise rejection tracking sample. By using a Map
instead of array we can get O(1) deletion and actually record the errors.
And, fixed indentation and backtick usage there to align with the rest of
the document.

Reviewed-By: Stephan Belanger <admin@stephenbelanger.com>
Reviewed-By: Petka Antonov <petka.antonov@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
PR-URL: #3438
@MylesBorins
Copy link
Contributor

should this be in lts?

@jasnell

@jasnell
Copy link
Member

jasnell commented Oct 23, 2015

Could be but not a pressing need.
On Oct 23, 2015 11:55 AM, "Myles Borins" notifications@github.com wrote:

should this be in lts?

@jasnell https://github.com/jasnell


Reply to this email directly or view it on GitHub
#3438 (comment).

domenic added a commit that referenced this pull request Oct 30, 2015
This adds an example of a semi-common promise pattern that could
result in false-positive 'unhandledRejection' events, to help motivate
why there is a dual 'rejectionHandled' event. I anticipate it being
helpful to users who encounter 'unhandledRejection' events that do not
stem from obvious typos such as the JSON.pasre example.

Also cleans up the promise rejection tracking sample. By using a Map
instead of array we can get O(1) deletion and actually record the errors.
And, fixed indentation and backtick usage there to align with the rest of
the document.

Reviewed-By: Stephan Belanger <admin@stephenbelanger.com>
Reviewed-By: Petka Antonov <petka.antonov@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
PR-URL: #3438
@jasnell
Copy link
Member

jasnell commented Oct 30, 2015

Landed in v4.x-staging in bb73029

domenic added a commit that referenced this pull request Oct 30, 2015
This adds an example of a semi-common promise pattern that could
result in false-positive 'unhandledRejection' events, to help motivate
why there is a dual 'rejectionHandled' event. I anticipate it being
helpful to users who encounter 'unhandledRejection' events that do not
stem from obvious typos such as the JSON.pasre example.

Also cleans up the promise rejection tracking sample. By using a Map
instead of array we can get O(1) deletion and actually record the errors.
And, fixed indentation and backtick usage there to align with the rest of
the document.

Reviewed-By: Stephan Belanger <admin@stephenbelanger.com>
Reviewed-By: Petka Antonov <petka.antonov@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
PR-URL: #3438
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
doc Issues and PRs related to the documentations. process Issues and PRs related to the process subsystem.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants