-
Notifications
You must be signed in to change notification settings - Fork 3
Should once
functions be marked as such, so libraries can throw them away?
#6
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
Comments
Functions produced by |
I suppose one may view once functions as state machines with two states—uncalled and already-called. In that sense, once functions are different from bound functions, async functions, etc. It may be reasonable to mark them and not the other sorts of function for that reason. |
Generators also are state machines - but we have no way to find is this |
Although that is true, there is no way to generally determine the state of a once function without an extra property. With generators, you can at least use check There is some precedent in some userland libraries, too; for example, onetime has a I’m neutral about this myself, personally, but it’s worth at least considering. |
That mean calling of the the generator if it's not Another precedent is the state of promises. I'm not against of this - I'm just for following the same design in the language everywhere without changing this design each new proposal (for the same reason I'm for the |
Note that, although it is true that In contrast, when you call a function In order to tell whether …Having said that, it may be the case that we don’t even want to design this |
@keithamus: If we do end up supporting argument passing to callbacks (maybe using the approach in #5 (comment)), then I think that it would be good to allow any function—not just functions created by Maybe something like this: const ƒOnce = ƒ.once();
ƒOnce.noop; // This is false.
fOnce(5);
ƒOnce.true; // This is now true. An event system could check a callback’s class EventEmitter {
// This is a Set of event handlers.
#handlerSet;
addHandler (callback) {
const handler = event => {
callback(event);
// Note that the callback is expected to set its noop property before it
// returns, even if the callback is asynchronous.
if (callback.noop) {
this.#handlerSet.remove(handler);
}
});
this.#handlerSet.add(handler);
}
emit (event) {
for (const handler of this.#handlerSet) {
handler(event);
}
}
} |
imo this is a nonstarter. In the same way as we don't have an "is this an async function" idiom in the language - because |
@ljharb: The idea of my snippet above was that any function—not just functions created by For example, the function below does not use let n = 0;
function printUpToThreeTimes () {
if (n < 3) {
console.log(n);
n ++;
} else {
printUpToThreeTimes.noop = true;
}
} Adding this to an event system would allow the event system to know when the function will no longer do anything and remove the function from itself for garbage collection. eventSystem.addHandler(printUpToThreeTimes);
eventSystem.emit(); // Prints 0.
eventSystem.emit(); // Prints 1.
eventSystem.emit(); // Prints 2.
eventSystem.emit(); // Does nothing *and* `printUpToThreeTimes` is removed from `eventSystem`’s handlers. |
Sure, but then a function could lie - a multi-use function could claim to be "once", and a one-use function could fail to mark itself as "once". |
Indeed, the function can lie; the contract would be an honor system between the creators of the function and the event system. If The question here is whether we want functions created by |
What’s the point of standardizing an honor system? |
I suppose it’s not really an “honor system” either. That is to say, the meaning of the let n = 0, arr = [];
function addToArrUpToThreeTimes () {
// This side effect occurs even if .noop is true,
// because the function’s author consider it to be insignificant
// compared to adding values to arr.
console.log(n);
if (n < 3) {
n ++;
} else {
addToArrUpToThreeTimes.noop = true;
}
}
addToArrUpToThreeTimes(0); // Prints 0; arr is now [0]; noop flag is false.
addToArrUpToThreeTimes(1); // Prints 1; arr is now [0, 1]; noop flag is false.
addToArrUpToThreeTimes(2); // Prints 2; arr is now [0, 1, 2]; noop flag is true.
addToArrUpToThreeTimes(3); // Prints 3; arr is still [0, 1, 2]; noop flag is true. The meaning of the “noop” flag would be in the eye of the function’s author. A function can choose to set the flag on itself at any time, whether or not subsequent calls will cause literally no side effects. All the flag actually means is that the author considers any side effects by further function calls to be insignificant, and that the function is ready to be removed by any handler. But although I do find this to be an interesting idea, I don’t feel super strongly about this. Developers can write And we could always add this property in the future in a follow-on proposal. |
One common technique for using
once
is with the Event pattern, which allows for subscription to an event as a one-time acknowledgement, as opposed to seeing every event. Node.js hasEventEmitter.prototype.once
, and the DOM Standard hasonce
as an option toaddEventListener
.With regard to the event listener pattern, exposing to the listeners that this function is only desired to run once allows these implementations to cleanup after the initial event. For example NodeJS calls
removeListener
before dispatching to the function, ensuring the given function won't be called subsequent times, but also freeing up memory for weakly held references to the function:If
Function.prototype.once
is exposed to developers, I imagine a preference may emerge that rather than using.once(foo)
, instead.on(foo.once())
is used. In this case, without a marker to determine this function is a "once function", implementation like Node's will not be able to remove the event listener.I believe implementations should be able to tell a "once function" apart from a regular function.
Some possible ideas:
Function.isOnce(fn: Function): boolean
Function.prototype[Symbol.isOnce]: boolean
The text was updated successfully, but these errors were encountered: