-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Editorial: modernize timers spec #7348
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
Changes from 1 commit
1522d45
5e31c98
013e6df
e943b2b
bc078a5
185ccd2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -96441,35 +96441,26 @@ enum <dfn enum>DOMParserSupportedType</dfn> { | |
| <code>WindowOrWorkerGlobalScope</code> mixin, and each <span data-x="map value">value</span> is a | ||
| <code>DOMHighResTimeStamp</code>, representing the expiry time for that timer.</p> | ||
|
|
||
| <p>To get the <dfn export>list of active timers</dfn> for <code>WindowOrWorkerGlobalScope</code> | ||
| <var>global</var>, return the result of <span data-x="map get the keys">getting the keys</span> | ||
| for <var>global</var>'s <span>map of active timers</span>.</p> | ||
|
|
||
| <hr> | ||
|
|
||
| <p>The <dfn method for="WindowOrWorkerGlobalScope" data-x="dom-setTimeout"><code | ||
| id="dom-windowtimers-setTimeout">setTimeout(<var>handler</var>, <var>timeout</var>, | ||
| ...<var>arguments</var>)</code></dfn> method must return the value returned by the <span>timer | ||
| initialization steps</span>, passing them the method's arguments, the object on which the method | ||
| for which the algorithm is running is implemented (a <code>Window</code> or | ||
| <code>WorkerGlobalScope</code> object) as the <var>method context</var>, and the <var>repeat</var> | ||
| flag set to false.</p> | ||
| ...<var>arguments</var>)</code></dfn> method steps are to return the result of running the | ||
| <span>timer initialization steps</span> given <span>this</span>, <var>handler</var>, | ||
| <var>timeout</var>, <var>arguments</var>, and false.</p> | ||
|
|
||
| <p>The <dfn method for="WindowOrWorkerGlobalScope" data-x="dom-setInterval"><code | ||
| id="dom-windowtimers-setInterval">setInterval(<var>handler</var>, <var>timeout</var>, | ||
| ...<var>arguments</var>)</code></dfn> method must return the value returned by the <span>timer | ||
| initialization steps</span>, passing them the method's arguments, the object on which the method | ||
| for which the algorithm is running is implemented (a <code>Window</code> or | ||
| <code>WorkerGlobalScope</code> object) as the <var>method context</var>, and the <var>repeat</var> | ||
| flag set to true.</p> | ||
| ...<var>arguments</var>)</code></dfn> method steps are to return the result of running the | ||
| <span>timer initialization steps</span> given <span>this</span>, <var>handler</var>, | ||
| <var>timeout</var>, <var>arguments</var>, and true.</p> | ||
|
|
||
| <p>The <dfn method for="WindowOrWorkerGlobalScope" data-x="dom-clearTimeout"><code | ||
| id="dom-windowtimers-clearTimeout">clearTimeout(<var>handle</var>)</code></dfn> and <dfn method | ||
| for="WindowOrWorkerGlobalScope" data-x="dom-clearInterval"><code | ||
| id="dom-windowtimers-clearInterval">clearInterval(<var>handle</var>)</code></dfn> methods must | ||
| <span data-x="map remove">remove</span> <span>map of active timers</span>[<var>handle</var>] of | ||
| the <code>WindowOrWorkerGlobalScope</code> object on which the method was invoked, if any, where | ||
| <var>handle</var> is the argument passed to the method.</p> | ||
| id="dom-windowtimers-clearInterval">clearInterval(<var>handle</var>)</code></dfn> method steps | ||
| are to <span data-x="map remove">remove</span> <span>this</span>'s <span>map of active | ||
| timers</span>[<var>handle</var>].</p> | ||
|
|
||
| <p class="note">Because <code data-x="dom-clearTimeout">clearTimeout()</code> and <code | ||
| data-x="dom-clearInterval">clearInterval()</code> clear entries from the same map, either method | ||
|
|
@@ -96478,61 +96469,62 @@ enum <dfn enum>DOMParserSupportedType</dfn> { | |
|
|
||
| <hr> | ||
|
|
||
| <p>The <!--en-GB--><dfn id="timer-initialisation-steps">timer initialization steps</dfn>, which | ||
| are invoked with some method arguments, a <var>method context</var>, a <var>repeat</var> flag | ||
| which can be true or false, and optionally (and only if the <var>repeat</var> flag is true) a | ||
| <var>previous handle</var>, are as follows:</p> | ||
| <p>The <!--en-GB--><dfn id="timer-initialisation-steps">timer initialization steps</dfn>, given a | ||
| <code>WindowOrWorkerGlobalScope</code> <var>global</var>, a string or <code | ||
| data-x="idl-Function">Function</code> <var>handle</var>, a number <var>timeout</var>, a list | ||
| <var>arguments</var>, a boolean <var>repeat</var>, and optionally (and only if <var>repeat</var> | ||
| is true) a number <var>previousHandle</var>, are:</p> | ||
|
|
||
| <ol> | ||
| <li><p>Let <var>method context proxy</var> be <var>method context</var> if that | ||
| is a <code>WorkerGlobalScope</code> object, or else the <code>WindowProxy</code> that corresponds | ||
| to <var>method context</var>.</p></li> | ||
| <li><p>Let <var>thisArg</var> be <var>global</var> if that is a <code>WorkerGlobalScope</code> | ||
| object; otherwise let <var>thisArg</var> be the <code>WindowProxy</code> that corresponds to | ||
| <var>global</var>.</p></li> | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this instead be something like global's relevant Realm.[[GlobalEnv]].[[GlobalThisValue]]? (Elsewhere we use relevant Realm.[[GlobalEnv]]'s EnvironmentRecord's [[GlobalThisValue]] but JS itself does the shortcut there.)
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Possibly, but I'd kind of like to not spread that very-opaque pattern too far. Instead we should define a wrapper term that we can use. I'll file a separate issue for that. |
||
|
|
||
| <li><p>If <var>previous handle</var> was provided, let <var>handle</var> be <var>previous | ||
| handle</var>; otherwise, let <var>handle</var> be an <span>implementation-defined</span> integer | ||
| that is greater than zero that will identify the timeout to be set by this call in the <span>list | ||
| of active timers</span>.</p></li> | ||
| <li><p>If <var>previousHandle</var> was given, let <var>handle</var> be | ||
| <var>previousHandle</var>; otherwise, let <var>handle</var> be an | ||
| <span>implementation-defined</span> integer that is greater than zero that will identify the | ||
| timeout to be set by this call in <var>global</var>'s <span>map of active | ||
| timers</span>.</p></li> | ||
|
|
||
| <li><p>Let <var>callerRealm</var> be the <span>current Realm Record</span>, and | ||
| <var>calleeRealm</var> be <var>method context</var>'s <span>JavaScript realm</span>.</p></li> | ||
| <var>calleeRealm</var> be <var>global</var>'s <span data-x="concept-relevant-realm">relevant | ||
| Realm</span>.</p></li> | ||
|
|
||
| <li><p>Let <var>initiating script</var> be the <span>active script</span>.</p></li> | ||
|
|
||
| <li><p>Assert: <var>initiating script</var> is not null, since this algorithm is always called | ||
| from some script.</p></li> | ||
|
|
||
| <li> | ||
| <p>Let <var>task</var> be a <span data-x="concept-task">task</span> that runs the | ||
| following substeps:</p> | ||
| <p>Let <var>task</var> be a <span data-x="concept-task">task</span> that runs the following | ||
| substeps:</p> | ||
|
|
||
| <ol> | ||
| <li><p>If <var>handle</var> does not <span data-x="map exists">exist</span> in | ||
| <var>method context</var>'s <span>map of active timers</span>, then abort these steps.</p></li> | ||
| <var>global</var>'s <span>map of active timers</span>, then abort these steps.</p></li> | ||
|
|
||
| <li> | ||
| <p>Run the appropriate set of steps from the following list:</p> | ||
|
|
||
| <dl class="switch"> | ||
| <dt>If the first method argument is a <code data-x="idl-Function">Function</code></dt> | ||
| <dd><p><span data-x="es-invoking-callback-functions">Invoke</span> the <code | ||
| data-x="idl-Function">Function</code>. Use the third and subsequent method arguments (if any) | ||
| as the arguments for invoking the <code data-x="idl-Function">Function</code>. Use | ||
| <var>method context proxy</var> as the <span data-x="dfn-callback-this-value">callback this | ||
| value</span>. If this throws an exception, catch it, and <span>report the | ||
| exception</span>.</p></dd> | ||
| <dt>If <var>handler</var> is a <code data-x="idl-Function">Function</code></dt> | ||
| <dd><p><span data-x="es-invoking-callback-functions">Invoke</span> <var>handler</var> given | ||
| <var>arguments</var> with the <span data-x="dfn-callback-this-value">callback this | ||
| value</span> set to <var>thisArg</var>. If this throws an exception, catch it, and | ||
| <span>report the exception</span>.</p></dd> | ||
|
|
||
| <dt>Otherwise</dt> | ||
| <dd> | ||
| <ol> | ||
| <li><p>Assert: <var>handler</var> is a string.</p></li> | ||
|
|
||
| <li><p>Perform <span | ||
| data-x="the-hostensurecancompilestrings-implementation">HostEnsureCanCompileStrings</span>(<var>callerRealm</var>, | ||
| <var>calleeRealm</var>). If this throws an exception, catch it, <span>report the | ||
| exception</span>, and abort these steps.</p></li> | ||
|
|
||
| <li><p>Let <var>script source</var> be the first method argument.</p></li> | ||
|
|
||
| <li><p>Let <var>settings object</var> be <var>method context</var>'s <span>environment | ||
| settings object</span>.</p></li> | ||
| <li><p>Let <var>settings object</var> be <var>global</var>'s <span>relevant settings | ||
| object</span>.</p></li> | ||
|
|
||
| <li><p>Let <var>base URL</var> be <var>initiating script</var>'s <span | ||
| data-x="concept-script-base-url">base URL</span>.</p></li> | ||
|
|
@@ -96559,12 +96551,12 @@ enum <dfn enum>DOMParserSupportedType</dfn> { | |
| <p class="note">The effect of these options ensures that the string compilation done by | ||
| <code data-x="dom-setTimeout">setTimeout()</code> and <code | ||
| data-x="dom-setInterval">setInterval()</code> behaves equivalently to that done by | ||
| <code>eval()</code>. That is, <span>module script</span> fetches via <code>import()</code> | ||
| will behave the same in both contexts.</p> | ||
| <code>eval()</code>. That is, <span>module script</span> fetches via | ||
| <code>import()</code> will behave the same in both contexts.</p> | ||
| </li> | ||
|
|
||
| <li><p>Let <var>script</var> be the result of <span>creating a classic script</span> given | ||
| <var>script source</var>, <var>settings object</var>, <var>base URL</var>, and <var>fetch | ||
| <var>handler</var>, <var>settings object</var>, <var>base URL</var>, and <var>fetch | ||
| options</var>.</p></li> | ||
|
|
||
| <li><p><span data-x="run a classic script">Run the classic script</span> | ||
|
|
@@ -96574,105 +96566,104 @@ enum <dfn enum>DOMParserSupportedType</dfn> { | |
| </dl> | ||
| </li> | ||
|
|
||
| <li><p>If the <var>repeat</var> flag is true, then call <span>timer initialization | ||
| steps</span> again, passing them the same method arguments, the same <var>method | ||
| context</var>, with the <var>repeat</var> flag still set to true, and with the <var>previous handle</var> set to <var>handler</var>.</p></li> | ||
| <li><p>If <var>repeat</var> is true, then perform the <span>timer initialization | ||
| steps</span> again, given <var>global</var>, <var>handle</var>, <var>arguments</var>, true, and | ||
domenic marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| <var>handle</var>.</p></li> | ||
| </ol> | ||
| </li> | ||
|
|
||
| <li><p>Let <var>timeout</var> be the second method argument.</p></li> | ||
|
|
||
| <li> | ||
| <p>If the currently running <span data-x="concept-task">task</span> is a task that was created | ||
| by this algorithm, then let <var>nesting level</var> be the <span | ||
| data-x="concept-task">task</span>'s <span>timer nesting level</span>. Otherwise, let | ||
| <var>nesting level</var> be zero.</p> | ||
| <p>If the <span>surrounding agent</span>'s <span data-x="concept-agent-event-loop">event | ||
| loop</span>'s <span>currently running task</span> is a task that was created by this algorithm, | ||
| then let <var>nesting level</var> be the <span data-x="concept-task">task</span>'s <span>timer | ||
| nesting level</span>. Otherwise, let <var>nesting level</var> be zero.</p> | ||
|
|
||
| <p class="note">The task's <span>timer nesting level</span> is used both for nested calls to | ||
| <code data-x="dom-setTimeout">setTimeout()</code>, and for the repeating timers created by <code | ||
| data-x="dom-setInterval">setInterval()</code>. (Or, indeed, for any combination of the two.) In | ||
| other words, it represents nested invocations of this algorithm, not of a particular method.</p> | ||
| <code data-x="dom-setTimeout">setTimeout()</code>, and for the repeating timers created by | ||
domenic marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| <code data-x="dom-setInterval">setInterval()</code>. (Or, indeed, for any combination of the | ||
| two.) In other words, it represents nested invocations of this algorithm, not of a particular | ||
| method.</p> | ||
| </li> | ||
|
|
||
| <li><p>If <var>timeout</var> is less than 0, then set <var>timeout</var> to 0.</p></li> | ||
|
|
||
| <li><p>If <var>nesting level</var> is greater than 5, and <var>timeout</var> is less than 4, then | ||
| set <var>timeout</var> to 4.</p></li> | ||
| <li><p>If <var>nesting level</var> is greater than 5, and <var>timeout</var> is less than 4, | ||
| then set <var>timeout</var> to 4.</p></li> | ||
|
|
||
| <li><p>Increment <var>nesting level</var> by one.</p></li> | ||
|
|
||
| <li><p>Let <var>task</var>'s <dfn>timer nesting level</dfn> be <var>nesting | ||
| level</var>.</p></li> | ||
| <li><p>Set <var>task</var>'s <dfn>timer nesting level</dfn> to <var>nesting level</var>.</p></li> | ||
|
|
||
| <li><p>Let <var>startTime</var> be the <span>current high resolution time</span>.</p></li> | ||
|
|
||
| <li><p><span data-x="map set">Set</span> <span>map of active timers</span>[<var>handle</var>] to | ||
| <var>startTime</var> plus <var>timeout</var>.</p></li> | ||
|
|
||
| <li><p>Return <var>handle</var>, and then continue running this algorithm | ||
| <span>in parallel</span>.</p></li> | ||
| <li><p><span data-x="map set">Set</span> <var>global</var>'s | ||
| <span>map of active timers</span>[<var>handle</var>] to <var>startTime</var> plus | ||
| <var>timeout</var>.</p></li> | ||
|
|
||
| <li> | ||
| <p>If <var>method context</var> is a <code>Window</code> object, wait until the | ||
| <code>Document</code> associated with <var>method context</var> has been <span>fully | ||
| active</span> for a further <var>timeout</var> milliseconds (not necessarily | ||
| consecutively).</p> | ||
|
|
||
| <p>Otherwise, <var>method context</var> is a <code>WorkerGlobalScope</code> object; | ||
| wait until <var>timeout</var> milliseconds have passed with the worker not suspended | ||
| (not necessarily consecutively).</p> | ||
| </li> | ||
|
|
||
| <li> | ||
| <p>Wait until any invocations of this algorithm that had the same <var>method | ||
| context</var>, that started before this one, and whose <var>timeout</var> is equal to | ||
| or less than this one's, have completed.</p> | ||
| <p>Run the following steps <span>in parallel</span>:</p> | ||
|
|
||
| <p class="note">Argument conversion as defined by Web IDL (for example, invoking <code | ||
| data-x="">toString()</code> methods on objects passed as the first argument) happens in the | ||
| algorithms defined in Web IDL, before this algorithm is invoked.</p> | ||
| <ol> | ||
| <li> | ||
| <p>If <var>global</var> is a <code>Window</code> object, wait until <var>global</var>'s <span | ||
| data-x="concept-document-window">associated <code>Document</code></span> has been <span>fully | ||
| active</span> for a further <var>timeout</var> milliseconds (not necessarily | ||
| consecutively).</p> | ||
|
|
||
| <p>Otherwise, <var>global</var> is a <code>WorkerGlobalScope</code> object; wait | ||
| until <var>timeout</var> milliseconds have passed with the worker not suspended (not | ||
| necessarily consecutively).</p> | ||
| </li> | ||
|
|
||
| <div class="example"> | ||
| <li><p>Wait until any invocations of this algorithm that had the same <var>global</var>, that | ||
| started before this one, and whose <var>timeout</var> is equal to or less than this one's, | ||
| have completed.</p></li> | ||
|
|
||
| <p>So for example, the following rather silly code will result in the log containing "<code | ||
| data-x="">ONE TWO </code>":</p> | ||
| <li> | ||
| <p>Optionally, wait a further <span>implementation-defined</span> length of time.</p> | ||
|
|
||
| <pre><code class="js">var log = ''; | ||
| function logger(s) { log += s + ' '; } | ||
| <p class="note">This is intended to allow user agents to pad timeouts as needed to optimize | ||
| the power usage of the device. For example, some processors have a low-power mode where the | ||
| granularity of timers is reduced; on such platforms, user agents can slow timers down to fit | ||
| this schedule instead of requiring the processor to use the more accurate mode with its | ||
| associated higher power usage.</p> | ||
| </li> | ||
|
|
||
| setTimeout({ toString: function () { | ||
| setTimeout("logger('ONE')", 100); | ||
| return "logger('TWO')"; | ||
| } }, 100);</code></pre> | ||
| <li> | ||
| <p><span>Queue a global task</span> on the <dfn>timer task source</dfn> given | ||
| <var>global</var> to run <var>task</var>.</p> | ||
|
|
||
| </div> | ||
| <p class="note">Once the task has been processed, if <var>repeat</var> is false, it is safe | ||
| to remove the entry for <var>handle</var> from the <span>map of active timers</span> (there | ||
| is no way for the entry's existence to be detected past this point, so it does not | ||
| technically matter one way or the other).</p> | ||
| </li> | ||
| </ol> | ||
| </li> | ||
|
|
||
| <li> | ||
| <p>Optionally, wait a further <span>implementation-defined</span> length of time.</p> | ||
| <li><p>Return <var>handle</var>.</p></li> | ||
| </ol> | ||
|
|
||
| <p class="note">This is intended to allow user agents to pad timeouts as needed to optimize the | ||
| power usage of the device. For example, some processors have a low-power mode where the | ||
| granularity of timers is reduced; on such platforms, user agents can slow timers down to fit | ||
| this schedule instead of requiring the processor to use the more accurate mode with its | ||
| associated higher power usage.</p> | ||
| </li> | ||
| <p class="note">Argument conversion as defined by Web IDL (for example, invoking <code | ||
| data-x="">toString()</code> methods on objects passed as the first argument) happens in the | ||
| algorithms defined in Web IDL, before this algorithm is invoked.</p> | ||
|
|
||
| <li> | ||
| <p><span>Queue a global task</span> on the <dfn>timer task source</dfn> given <var>method | ||
| context</var> to run <var>task</var>.</p> | ||
| <div class="example"> | ||
| <p>So for example, the following rather silly code will result in the log containing "<code | ||
| data-x="">ONE TWO </code>":</p> | ||
|
|
||
| <p class="note">Once the task has been processed, if the <var>repeat</var> flag is | ||
| false, it is safe to remove the entry for <var>handle</var> from the <span>list of | ||
| active timers</span> (there is no way for the entry's existence to be detected past this point, | ||
| so it does not technically matter one way or the other).</p> | ||
| </li> | ||
| </ol> | ||
| <pre><code class="js">var log = ''; | ||
| function logger(s) { log += s + ' '; } | ||
|
|
||
| setTimeout({ toString: function () { | ||
| setTimeout("logger('ONE')", 100); | ||
| return "logger('TWO')"; | ||
| } }, 100);</code></pre> | ||
| </div> | ||
|
|
||
| </div> | ||
|
|
||
| <div class="example"> | ||
|
|
||
| <p>To run tasks of several milliseconds back to back without any delay, while still yielding back | ||
| to the browser to avoid starving the user interface (and to avoid the browser killing the script | ||
| for hogging the CPU), simply queue the next timer before performing work:</p> | ||
|
|
@@ -96697,7 +96688,6 @@ function scheduleWork() { | |
| } | ||
|
|
||
| scheduleWork(); // queues a task to do lots of work</code></pre> | ||
|
|
||
| </div> | ||
|
|
||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.