I have a search input field in a ScalaJS app that fires off requests to a backend server whilst the user types in a city name. However, I need to implement a delay so that the request is not fired until after a certain delay (say 1000ms). Without such a delay, there is the chance that I'll get back false positives on the search (E.G. If the user wants to search for "paris", then there will be a false hit on "par" - a small town in Cornwall, England - when the third character is entered)
I've tried transcribing the JavaScript equivalent into Scala, but the setTimeout part doesn't seem to work.
import scala.scalajs.js.timers.{SetTimeoutHandle, clearTimeout, setTimeout}
private def delay = () => {
// Set initial timeout to do nothing after 0 ms
var handle: SetTimeoutHandle = setTimeout(0)(() => {})
(fn: Function0[Unit], ms: Double) => {
clearTimeout(handle)
handle = setTimeout(ms)(fn)
}
}
Then I'm handling the user input event using an Akka Actor
def receive = {
/************************************************
* Client event
* The user has typed something into the search field
*/
case evt: Event =>
delay()(handleInput, 1000.0)
}
Where handleInput
is the zero parameter function that obtains the user's input and then fires off a request to the backend.
The anonymous inner function that clears and then resets the timeout is executed, but the handleInput function never gets called
Thanks
Chris W
The problem in your code is that you are giving a function of type () => Unit
to setTimeout
, but setTimeout
takes a by-name parameter. In other words, the argument to setTimeout
should be a statement to execute when the timeout expires. If you give it a function, then after the timeout that function value will be evaluated, but the function will not be called!
It is similar to mistakenly trying to do
val result = fn // result is the *function* itself, but does not call it
instead of
val result = fn() // fn is called, result is what it returns
You can fix your call to setTimeout
by replacing fn
by fn()
. Also, it is typically more idiomatic, in those circumstances, to use {}
instead of ()
for the parameter to setTimeout
, which also gives a visual clue that it is a by-name parameter:
(fn: Function0[Unit], ms: Double) => {
clearTimeout(handle)
handle = setTimeout(ms) {
fn()
}
}
You should also adapt your first dummy setTimeout
for consistency, although since it is a dummy anyway, that will not change the behavior:
// Set initial timeout to do nothing after 0 ms
var handle: SetTimeoutHandle = setTimeout(0) {}