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

Support waiting for multiple event emissions #15

Merged
merged 16 commits into from Nov 18, 2018
Merged

Support waiting for multiple event emissions #15

merged 16 commits into from Nov 18, 2018

Conversation

szmarczak
Copy link
Contributor

Fixes #3

@sindresorhus
Copy link
Owner

Any reason you went away from using a separate method .multi(), like in your proposal in #3? Is not weird that an option can change the return value of the function? Especially weird that setting resolveImmediately suddenly makes it return an array. For example, let's say someone decides to dynamically generate the value for the count option. It's very easy to forget to account for that if the generated count is suddenly 1, the return type changes.

@sindresorhus sindresorhus changed the title Introduce the count and resolveImmediately option Add count and resolveImmediately option Nov 1, 2018
@szmarczak
Copy link
Contributor Author

Any reason you went away from using a separate method .multi(), like in your proposal in #3? Is not weird that an option can change the return value of the function?

You use .multi() when you the count is greater than 1. For 1 you don't use multi. But is .multi() needed? It's obvious that if count > 1 it returns an array.

Especially weird that setting resolveImmediately suddenly makes it return an array.

It isn't weird. There's no sense using count: 1, resolveImmediately: true. You use resolveImmediately when you want to access items before you got the count.

const data = await pEvent(emitter, event, {resolveImmediately: true});
// 0s later => []
// 1s later => ['a']
// 2s later => ['a', 'b']
// 3s later => ['a', 'b', 'c']
// 4s later => ['a', 'b', 'c', 'd']

resolveImmediately: true and count: 1:

const data = await pEvent(emitter, event, {count: 1, resolveImmediately: true});
// 0s later => []
// 1s later => ['a']

You can achieve the same by:

const data = await pEvent(emitter, event); // 'a'

For example, let's say someone decides to dynamically generate the value for the count option.

const data = pEvent(emitter, event, {count: Infinity, resolveImmediately: true});
const result = await data;
setTimeout(() => data.cancel(), n);

It's very easy to forget to account for that if the generated count is suddenly 1, the return type changes.

Answer:

It's obvious that if count > 1 it returns an array.

How do you imagine storing a few items in one item? :P You have to use an array.

@szmarczak
Copy link
Contributor Author

szmarczak commented Nov 1, 2018

I can do .multi() (then pEvent() would throw if passed count or resolveImmediately option) but there's no need. It requires the count option anyway, right?

@sindresorhus
Copy link
Owner

I just think the separation would be good. count and resolveImmediately would then be .multi-only options.

@sindresorhus
Copy link
Owner

It's obvious that if count > 1 it returns an array.

You say that, but is it really obvious when reading the code. If I see one call that has {count: 3} and one that has {count: 1}, I would, from just reading the code, assume both would return an array.

@sindresorhus
Copy link
Owner

Swift has taught me the value of having a single return value. It's much more predictable and readable.

@sindresorhus
Copy link
Owner

For example, let's say someone decides to dynamically generate the value for the count option.

const data = pEvent(emitter, event, {count: Infinity, resolveImmediately: true});
const result = await data;
setTimeout(() => data.cancel(), n);

This does not address my concern. You are not generating count dynamically there.

@sindresorhus
Copy link
Owner

Having had some time to think about this today, I'm even more convinced now that a separate method is the right call. Let's call it .multiple() instead though. No point in shortening it.

@szmarczak
Copy link
Contributor Author

This does not address my concern. You are not generating count dynamically there.

Do you mean passing a function?

Having had some time to think about this today, I'm even more convinced now that a separate method is the right call. Let's call it .multiple() instead though. No point in shortening it.

Will do :)

@sindresorhus
Copy link
Owner

Do you mean passing a function?

Yes:

const data = await pEvent(emitter, event, {count: generateRandomCount()});

It's not clear from reading this that data could sometimes be a value and sometimes and array.

@szmarczak
Copy link
Contributor Author

szmarczak commented Nov 3, 2018

const data = await pEvent(emitter, event, {count: generateRandomCount()});

do you mean
-const data = await pEvent(emitter, event, {count: generateRandomCount()});
+const data = await pEvent(emitter, event, {count: generateRandomCount});

NEVERMIND, I got it now! :D

(by dynamically first I understood it as passing a function to the count option :P it is just the user won't know if it will be an array or a function, cuz generateRandomCount value may be different, right?)

@sindresorhus
Copy link
Owner

it is just the user won't know if it will be an array or a function, cuz generateRandomCount value may be different, right?)

Correct

@szmarczak
Copy link
Contributor Author

Maybe we should introduce it as an option?

pEvent(emitter, 'event', {
	multiple: true,
	count: 2
}); // returns ['a', 'b']
pEvent(emitter, 'event', {
	multiple: false,
	count: 2
}); // waits for two events, but returns only the first one

Also would get rid of these lines of code:

p-event/index.js

Lines 88 to 96 in 23481b5

options = Object.assign({}, options, {
count: 1,
resolveImmediately: false
});
const arrayPromise = multiple(emitter, event, options);
const promise = arrayPromise.then(array => array[0]);
promise.cancel = arrayPromise.cancel;

@sindresorhus
Copy link
Owner

I still think it should be a separate method. I considered a multiple option, but then it would conflict with the resolveImmediately option and cause ambiguity with the count option. I just feel it's cleaner to separate them.

waits for two events, but returns only the first one

I don't see the point of this. If you want to wait for two event, but only use the first event, you just do events[0].

Also would get rid of these lines of code:

I'd rather optimize for the public API than internal reducing code.

@szmarczak
Copy link
Contributor Author

szmarczak commented Nov 6, 2018

Then the PR's ready to be reviewed :)

@sindresorhus
Copy link
Owner

sindresorhus commented Nov 7, 2018

You need to update the first paragraphs in the readme, as they assume support for only a single event.

readme.md Outdated
Type: `boolean`<br>
Default: `false`

States if the promise should resolve immediately. Emitting one of the `rejectionEvents` won't throw an error. Example:
Copy link
Owner

Choose a reason for hiding this comment

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

This should provide a use-case for the option, as it's not immediately clear, and also clarify that the returned array will be mutated. when new events are emitted.

test.js Show resolved Hide resolved
readme.md Show resolved Hide resolved
index.js Show resolved Hide resolved
@sindresorhus sindresorhus changed the title Add count and resolveImmediately option Support waiting for multiple event emissions Nov 7, 2018
sindresorhus and others added 2 commits November 7, 2018 17:58
Co-Authored-By: szmarczak <36894700+szmarczak@users.noreply.github.com>
index.js Outdated
}, options);
if (isNaN(options.count) || options.count < 1) {
throw new TypeError('`count` option should be a number greater than 1');
}
Copy link
Owner

Choose a reason for hiding this comment

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

This needs to be inside the new Promise or return a Promise.reject(). Methods that return a promise should always do so, even in the failure case.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No. You're wrong. The promise executes a task, does not generate it. We need to verify options in the promise generator, we need to verify the options before a task is scheduled.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

const foo = pThrottle(invaidOptions); // this wouldn't throw
foo (); // this would throw and should NOT

Copy link
Owner

Choose a reason for hiding this comment

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

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh, you're right. Sorry, I just got confused (cuz I work on many things at the same time and my mind is mixed up) :p

index.js Outdated Show resolved Hide resolved
index.js Outdated
multiArgs: false
}, options);
if (isNaN(options.count) || options.count < 1) {
throw new TypeError('`count` option should be a number greater than 1');
Copy link
Owner

Choose a reason for hiding this comment

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

I don't see the point of not allowing 0 and 1 as count. That just makes it harder to use this API programmatically with unexpected count numbers.

Suggested change
throw new TypeError('`count` option should be a number greater than 1');
throw new TypeError('The `count` option should be a positive integer or Infinity');

The count option should also be documented for the above, that it accepts a positive integer or Infinity.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Also, why have you removed the article from the test name?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, so count >= 0 it is.

Copy link
Owner

Choose a reason for hiding this comment

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

Also, why have you removed the article from the test name?

I felt it was moot.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah, just a shortcut? :P

readme.md Outdated Show resolved Hide resolved
sindresorhus and others added 3 commits November 8, 2018 07:48
Co-Authored-By: szmarczak <36894700+szmarczak@users.noreply.github.com>
@sindresorhus sindresorhus merged commit b902979 into sindresorhus:master Nov 18, 2018
@sindresorhus
Copy link
Owner

Perfect 👌

@raymondfeng
Copy link
Contributor

raymondfeng commented Nov 18, 2018

Nitpick: options.batchSize might be a better name for options.count as it hints that we deliver multiple events in batch of the given size. Similarly, pEvent.batch() might be easier to understand.

@mysticatea
Copy link
Sponsor

Has this feature released? I have encountered TypeError on pEvent.multiple(), on p-event@2.1.0.

@kevva
Copy link
Contributor

kevva commented Jan 20, 2019

Has this feature released? I have encountered TypeError on pEvent.multiple(), on p-event@2.1.0.

No, not yet. Latest release was in June.

@sindresorhus
Copy link
Owner

It seems I just forgot to release it. Duuh. Done now.

https://github.com/sindresorhus/p-event/releases/tag/v2.2.0

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.

None yet

5 participants