1

While a lot of questions and answers tell me WHY and WHAT the setTimeout(0) is for, I cannot find a very good alternative for it.

My problem

I have a click handler, wich executes a function that signals another class to update its status.

That click handler is on the parent element.

I have a checkbox inside that element, wich gets checked or unchecked.

Because the click handler is on the parent element, that gets called first. But I need my checkbox to change status before the signal is send.

So I use setTimeout(0) to prevent the signal to be send before the checkbox is checked.

HTML

<div click.delegate="update()">
    <div class="checkbox">
        <label>
            <input checked.bind="group.validated" type="checkbox"> Visibility
        </label>
    </div>
</div>

Javascript

update(){
    setTimeout(()=>{
        this.signaler.signal('refresh-groups');
    }, 0);
    return true;
}

Basically, what is happening, is that the return true; is executed before the this.signaler.signal function. That way, the checkbox gets checked before the signal is send. Note this won't happen in regular onclick methods where the checkbox status is updated first, but this is the way the Aurelia framework behaves.

I don't like the feeling that I create here. A timeout of 0 seconds means that the function is at the end of the callstack, nothing more. If anything would happen to my return true; statement causing it to wait 0.1 second, I face the same problem.

Is there any alternative that is more trustworthy? Simply using a Promise doesnt seem to do the trick here.

Jeremy Danyow
  • 26,470
  • 12
  • 87
  • 133
Randy
  • 9,419
  • 5
  • 39
  • 56
  • For those who might not have known about `setTimeout(fn, 0)` (myself included), see [this link](http://stackoverflow.com/questions/779379/why-is-settimeoutfn-0-sometimes-useful) – Jonathan Lam Aug 09 '16 at 14:46
  • https://developer.mozilla.org/en/docs/Web/API/Window/setImmediate – MysterX Aug 09 '16 at 14:46
  • *"A timeout of 0 seconds means that the function is at the end of the callstack, nothing more. If anything would happen to my return true; statement causing it to wait 0.1 second, I face the same problem."* What do you mean, "you face the same problem"? Your `setTimeout` callback **will not** execute before your `update` method returns, no matter how long that takes. – T.J. Crowder Aug 09 '16 at 14:47
  • @MysterX that would have been a great alternative, but there is no function called. The only thing I wait for is the checkbox getting checked, so I cant run `window.clearImmediate` – Randy Aug 09 '16 at 14:47
  • @T.J.Crowder Thank you, I did not know that. I figured JS would simply continue execution, only the order was changed by the `setTimeout`. – Randy Aug 09 '16 at 14:49
  • @Randy: You may find [this answer](http://stackoverflow.com/a/38844376/157247) useful re the job queue used by JavaScript. – T.J. Crowder Aug 09 '16 at 14:51
  • So why are you not just listening for the change event? – epascarello Aug 09 '16 at 14:52
  • @epascarello Because the actual change event on the checkbox does not update my variable immediately, but that *is* what I am waiting for. It is the `checked.bind` that forces me into this `setTimeout()` – Randy Aug 09 '16 at 14:53
  • Btw, the underlying mechanism of `setTimeout` prepends the callback to the next iteration of the event loop. If you want it to be appended to the current iteration, use the `MutationObserver` API. –  Aug 09 '16 at 14:55
  • Instead of using `MutationObserver` directly, you could use `aurelia-task-queue` – Fabio Aug 09 '16 at 15:30

2 Answers2

1

I think the primary issue is you're using the click event which fires before the input element's value changes. Use the change event instead.

https://gist.run/?id=863282464762b54c8cf67de541bac4d3

Jeremy Danyow
  • 26,470
  • 12
  • 87
  • 133
  • Nice!!! Let me ask you a question Danyow. Would my answer also work in this case? – Fabio Aug 09 '16 at 15:36
  • @FabioLuz - It definitely would work. I try to avoid using the TaskQueue and Signaler where possible- there's usually a more natural way of doing things. – Jeremy Danyow Aug 09 '16 at 15:37
  • yeah, your solution is much more simpler (even though someone voted it down WTF?). I was trying to follow OP's logic, it's good to know that it would work (and it also was voted down ¯\_(ツ)_/¯). Thank you! – Fabio Aug 09 '16 at 15:46
  • This is epic, I never knew that you could hang a `change` handler on a regular element. – Randy Aug 09 '16 at 17:12
0

You could run this.signaler.signal() as Task. For instance:

import {BindingSignaler} from 'aurelia-templating-resources';
import { TaskQueue } from 'aurelia-task-queue';

export class App {

  static inject() { return [BindingSignaler, TaskQueue]; }

  constructor(signaler, taskQueue) {
    this.signaler = signaler;
    this.taskQueue = taskQueue;
  }

  update(){
    this.taskQueue.queueTask(() => {
        this.signaler.signal('refresh-groups');
    });

    return true;
  }
}

See this example https://gist.run/?id=88500143701dab0b0697b17a211af3a7

Fabio
  • 11,892
  • 1
  • 25
  • 41