Skip to main content

Coffeescript Polling Issues

Javascript • Asked by Chris Zempel

I'd like to reload an iframe when the images needed to generate the preview it will display are cached by background workers. However, I can't figure out how to get setTimeout() to work correctly (or any other method of polling), and keep making thousands of immediate requests to my local machine which crashes the browser. While this is fun, I'd like it work.

class Preview
  constructor: (preview) ->
    @preview = preview
    @setBehavior()

  setBehavior: ->
    @loadWhenCached(@preview) unless @preview.data.footprintsCached?

  loadWhenCached: (preview) =>
    console.log "polling"
    $.ajax(
      url: "/user_image_caches/cached",
      method: "GET"
      dataType: "JSON"
      success: (data) =>
        if data["cached"] == "true"
          console.log "wat"
          preview.attr('src', '/certificate.pdf')
    )
    unless preview.data?.footprintsCached?
      setTimeout @loadWhenCached(preview), 2000


jQuery ->
  new Preview $("[data-behavior='preview']").first()

You are calling loadWhenCached from inside loadWhenCached which is a recursive call. That's probably fine, but but you're not updating the footprintsCached value so it will always be false and it will always run again. Your success method needs to update the preview's footprintsCached attribute so that it can know not to do it again.

I'm curious why you need a loop at all? Issuing the first AJAX request should take care of it unless you have a reason that it's likely to fail. And if that's the case, you should consider pulling the loop out to a higher level rather than inside the method. That will help you manage it better so rather than making a recursive loop, you could do a setInterval that calls the method every couple seconds and then kills the interval once it has been successful.


One of the first SO results said "don't use setInterval," but that's exactly what I've been looking for.

The images will take anywhere between 15s and 120s to cache so this seems to be the most prudent way.

class Preview
  constructor: (preview) ->
    @preview = preview
    @interval = null
    @loadWhenCachedPoll() unless @preview.data?.footprintsCached?

  loadWhenCachedPoll: ->
    @interval = setInterval(@loadWhenCached(), 3000)

  loadWhenCached: ->
    console.log "polling"
    $.ajax(
      url: "/user_image_caches/cached",
      method: "GET"
      dataType: "JSON"
      success: (data) =>
        if data["cached"] == "true"
          clearInterval(@interval)
          @preview.data.footprintsCached.val("true")
          @preview.attr('src', '/certificate.pdf')
    )

jQuery ->
  new Preview $("[data-behavior='preview']")

It runs successfully the first time, then every time after that I get

Uncaught SyntaxError: Unexpected identifier

signaling that something fell out of scope. I'm thinking @interval or @loadWhenCached() fell out of scope, but I'm not sure why.


  loadWhenCached: ->

This might actually need to be a thick arrow so you can pass in @interval in the next callback too.


In coffeescript, when you call

@loadWhenCached()

it actually invokes the function. Because setInterval()'s context is the window, what happens is that it executed once successfully, then switch to the context of the window (where it no longer had access to @preview, or @interval) and started throwing that error.

What I needed to pass to the setInterval() was a reference to what function to invoke, not the actual invocation of the function.

@interval = setInterval(@loadWhenCached, 3000)

Additionally, I then needed to make sure that it was in the correct closure to grab stuff, meaning @loadWhenCached needs to use the fat => to ensure it takes place where it has access to @preview and @interval.


Ah yep, I overlooked the method call and thought you were just passing the reference. That'd do it!

Good debugging!


Wow, Chris is really becoming a rockstar! Good job!


Login or Create An Account to join the conversation.

Subscribe to the newsletter

Join 24,647+ developers who get early access to new screencasts, articles, guides, updates, and more.

    By clicking this button, you agree to the GoRails Terms of Service and Privacy Policy.

    More of a social being? We're also on Twitter and YouTube.