4

I'm working on a live photo stream app. Essentially, users will be uploading photos to a folder on my server via FTP, and the page should update anytime a new photo is added without refreshing.

I plan to do this with AJAX and the method suggested in this thread: How to check if directory contents has changed with PHP?. Essentially, I want to have a loop on my page that every X seconds, makes an AJAX call to a PHP page which gives back the MD5 hash of the directory listing for the uploads folder. If the hash has changed since the last call, another AJAX call will get the most recently added file and jQuery will display it on the page.

In vanilla Javascript/jQuery, this can be done using a recursive, named function with a setTimeout inside of it. This code is working for me:

function refreshLoop(currentFolderState, refreshRate) {
    // Get the new folder state
    $.get("../ajax/getFolderState.php", function(data) {
        // If it's different than the current state
        if ( data !== currentFolderState ) {
            // Do something
        }
        // If it's the same as the current state
        else {
            // Do nothing
        }
        // After the refresh rate, try again
        setTimeout(function() {
            refreshLoop(data, refreshRate);
        }, refreshRate);
    });
}

// Document Ready
$(function() {

    var refreshRate = 5000;

    // After refresh rate has passed
    setTimeout(function() {
        // Get the starting folder state
        $.get("../ajax/getFolderState.php", function(data) {
            // Kick off the loop
            refreshLoop(data, refreshRate);
        });
    }, refreshRate);

});

I'm using Coffeescript on this project in an attempt to learn how it works, since a lot of developers seem to be fond of it, but I can't figure out how to replicate this functionality without the use of named functions. Can someone either point me in the right direction or explain a better way for me to achieve this effect that can be easily done in Coffeescript?

Community
  • 1
  • 1
Ryan Giglio
  • 1,085
  • 1
  • 14
  • 26

4 Answers4

6

A more succinct version

do poll = ->
  #do work here
  setTimeout poll, 1000

which compiles to

var poll;
(poll = function() {
  //do work here
  return setTimeout(poll, 1000);
})();
kasperlanger
  • 61
  • 1
  • 3
  • FWIW this method seems to only be compatible with `->` and `=>` requires much more elaborate setup for context wrangling – Chris Marisic Jun 16 '16 at 19:19
4

You could do something like this in CoffeeScript:

refresh_loop = (data, refresh_rate) ->
    #...etc

refresh_rate = 5000
setTimeout((->
    $.get("../ajax/getFolderState.php", (data) ->
        refresh_loop(data, refresh_rate)
), refresh_rate)

Demo: http://jsfiddle.net/ambiguous/ZVTcg/

If your function was smaller then you could inline all of it like this:

refresh_rate = 5000
setTimeout(f = (->
    // real work goes here...
    setTimeout(f, refresh_rate)
), refresh_rate)

Demo: http://jsfiddle.net/ambiguous/XThV6/

Inlining all of it would probably be a bit ugly and confusing in your case though so using a separate refresh_loop = (data, refresh_rate) -> ... construct is probably a better idea.

mu is too short
  • 426,620
  • 70
  • 833
  • 800
  • I could have sworn I tried doing that at some point and it didn't work, but I guess I had something else fouled up also. Thanks! – Ryan Giglio Mar 08 '12 at 20:51
  • @Ryan: You probably left out the extra parentheses, the implied block structure of CoffeeScript makes a bit of a mess sometimes. – mu is too short Mar 08 '12 at 21:13
  • 1
    Perfect. The inline function is ready to go. Future readers if you're using any `@` references inside the `f =` function, you likely want to update it to `(=>` so it properly captures the original scope. – Chris Marisic Jun 16 '16 at 18:51
4

I don't see any problem here. All you have to do is assign refreshLoop to a variable. Here's a direct translation of your code to CoffeeScript:

    refreshLoop = (currentFolderState, refreshRate) ->
        $.get '../ajax/getFolderState.php', (date) ->
            # ...
            setTimeout (-> refreshLoop(data, refreshRate)), refreshRate

    $ ->
        refreshRate = 5000
        setTimeout (->
            $.get '../ajax/getFolderState.php', (data) ->
                refreshLoop data, refreshRate
        ), refreshRate
Trevor Burnham
  • 76,828
  • 33
  • 160
  • 196
  • I personally like to make an alias of the `setTimeout` method called `delay` with the parameter reversed like this: `delay = (ms, cb) -> setTimeout(cb, ms)` that way I can later call it using: `delay refreshRate, -> "do something..."` just seems a little nicer without the extra parenthesis. I think I even saw this originally from one of your responses. – Sandro Mar 08 '12 at 20:57
  • @Sandro Yeah, that definitely makes things nicer. I'm just trying to answer the question as directly as possible. – Trevor Burnham Mar 08 '12 at 21:35
  • This is exactly what I tried to do initially, I must have had a syntax error in there somewhere that messed me up. Also, that delay alias is a great idea and I think I may use that in the future. – Ryan Giglio Mar 08 '12 at 21:56
  • 1
    @RyanGiglio Did you try plugging your code into http://js2coffee.org/? It's not perfect, but it's a great starting point. – Trevor Burnham Mar 08 '12 at 22:13
  • Wow that looks like a great resource. Will bookmark it. Thanks! – Ryan Giglio Mar 09 '12 at 05:01
2

but I can't figure out how to replicate this functionality without the use of named functions.

You can use self-invoking anonymous function like this:

(function(){
   // do your stuff

   setTimeout(function(){
      arguments.callee();
   }, time);
})();

Here arguments.callee refers to anonymous function itself.

Please note that arguments.callee is deprecated in ES5, there is nothing wrong in using named function though.

Sarfraz
  • 377,238
  • 77
  • 533
  • 578
  • I found a suggestion about arguments.callee elsewhere also, but JSHint gave me a warning about it and I found a lot of suggestions that it was bad to use it in general. What is the potential issue that makes it sound so scary? – Ryan Giglio Mar 08 '12 at 20:15
  • 1
    The `arguments.callee` construct is deprecated. It also causes performance problems. – Pointy Mar 08 '12 at 20:16
  • Those are scary words to hear. – Ryan Giglio Mar 08 '12 at 20:19
  • @RyanGiglio:See this: http://stackoverflow.com/questions/103598/why-was-the-arguments-callee-caller-property-deprecated-in-javascript – Sarfraz Mar 08 '12 at 20:22
  • I see that you can name an inline function in vanilla Javascript (which is something I didn't know, so thank you!) but that still doesn't resolve my issue of accomplishing this in Coffeescript. As far as I know it has no method of naming functions at all, only binding anonymous functions to variables. – Ryan Giglio Mar 08 '12 at 20:29