Promises.delay vs Promises.tick

I see some examples like this one (Observables anti-patterns and code smells) which use this code with Promises.delay(...):

{
  let i = 0;
  yield i;
  while (true) {
    yield Promises.delay(1000, ++i);
  }
}

Then I see examples like this one (Introduction to Promises) with Promises.tick(...)

{
  let i = 0;
  while (true) {
    yield Promises.tick(1000, ++i);
  }
}

I noticed both the patterns above are similar and generate similar results as the cell evaluates…
Why use one over the other?

The Introduction to Promises post compares Promises.delay to Promises.tick, but I don’t understand how they’re different:

Promises.tick is similar (to Promises.delay), except it returns a promise that resolves at the next given interval.

I tried checking their implementations, but couldn’t see the details of Promises.tick:

Promises.delay.toString()
“function(e,t){return new Promise((function(n){setTimeout((function(){n(t)}),e)}))}”

Promises.tick.toString()
“function(e,t){return Zt(Math.ceil((Date.now()+1)/e)*e,t)}”

Is it similar to the difference between a recursive setTimeout and setInterval?

I tried making this notebook to check the difference, the values get further apart as I change focus away from the tab and back to the tab, is that relevant?

I don’t have ideas about tick vs delay, but I do have some relevant information about your last paragraph.

Every time a cell yields, the Observable runtime will wait until the next requestAnimationFrame before the cell gets another chance to execute. Most browsers will slow down requestAnimationFrame requests when the tab is not focused. So this is expected, but it probably makes your test less valid. I’d recommend keeping the tab focused while you are measuring the values.

1 Like

Great point from @mythmon

Every time a cell yields, the Observable runtime will wait until the next requestAnimationFrame before the cell gets another chance to execute.

I think this is mentioned in the Observable Introduction to Generators post (and maybe elsewhere?):

Observable pulls at most one value per animation frame (sixty times per second) from the generator…

The above implies Observable uses requestAnimationFrame – see the framerate mentioned in the requestAnimationFrame documentation itself

You should call (requetAnimationFrame) whenever you’re ready to update your animation onscreen. This will request that your animation function be called before the browser performs the next repaint. The number of callbacks is usually 60 times per second, but will generally match the display refresh rate in most web browsers as per W3C recommendation…

requestAnimationFrame() calls are paused in most browsers when running in background tabs or hidden <iframe>s in order to improve performance and battery life.

Maybe this property of requestAnimationFrame is related to the difference in Promises.tick vs Promises.delay… (for example, maybe Promises.tick will “keep going” despite the change in browser tab).

Or maybe it makes my notebook a bad test if I’m trying to understand the differences!

I’ll mark your answer correct if I don’t hear back.

Thank you,

You can find the implementations in Observable’s Standard Library.

The difference between the two is that Promises.tick() waits until the next multiple of your duration relative to the current time, which lets you account for variations in runtime between each yield.

In summary, I’d describe the difference inside loops as follows:

  • Promises.delay: Run something IN certain intervals:
    task=123ms + delay=1000ms + task=83ms + delay=1000ms + ...
    
  • Promises.tick: Run something AT certain intervals:
    task=123ms + tick=(1000-123)ms + task=83ms + tick=(1000-83)ms + ...
    

(Be aware that in practice the precision is lower.)

See the examples or documentation for more details.


As for framerate: Most browsers will throttle both requestAnimationFrame and setTimeout in the following two cases:

  • The user hasn’t interacted with the page yet (this applies especially to Safari)
  • The tab is in the background.

See this issue for more details.

If you need to process things in the background, you can try one of the many setImmediate polyfills which use the MessageChannel API under the hood. See this issue for more

1 Like

Thank you!

Let me repeat the distinction you phrased so nicely:

And let me reiterate the parts of your answer I should’ve tried:

Checking implementation in the code of the Observable standard library itself (vs trying to check .toString() technique, or Show function definition in Chrome devtools)

Checking the Observable standard library documentation (vs trying to check only Observable tutorials)

By the way, I think the distinction “Promises.delay IN intervals, vs Promises.tick AT intervals” is what this StackOverflow answer re: recursive setTimeout vs setInterval is trying to articulate (but the SO answer is hard to follow!).

That is, the time required to do the processing “between each yield” (aka the “function takes time to process”)

In other words

  • recursive setTimeout is comparable to Promises.delay,
  • and setInterval is comparable to Promises.tick

Because: setTimeout/Promises.delay will get pushed further out in time (2,206 ms; so its incremented variable i will be eventually be lower), because it waits for the stuff “between each yield” aka “the time it takes for the function to finish”:
task=123ms + delay=1000ms + task=83ms + delay=1000ms + ... = 2,206 ms

Whereas setInterval / Promises.tick will not get pushed as far out in time (2,000 ms; so its incremented value i will eventually be higher), because it *doesn’t wait for the stuff “between each yield”
task=123ms + tick=(1000-123)ms + task=83ms + tick=(1000-83)ms + ... = 2,000 ms

Keeping in mind what you said: