0

I'm making a small Node.js app for personal use that sideloads pics uploaded to my twitter account (pic.twitter.com links) to imgur using their anonymous API, replaces the pic.twitter.com links with the newly-obtained imgur links and saves them tweets to disk. Here's the bit of code that does this:

var 
    jsdom   = require( 'jsdom' )
,   request = require( 'request' )
,   t_pic   = /http:\/\/pic.twitter.com\/([a-z0-9]+)/gi;

function rehostImage( match ) {
    request( match, function( e, r, b ) {
        if ( e ) throw e;
        jsdom.env(
            {
                html: b,
                scripts: ['http://code.jquery.com/jquery-1.7.2.min.js']
            }, 
            function( err, window ) {
                var $ = window.jQuery;
                request.post( 
                    'http://api.imgur.com/2/upload.json', 
                    { 
                        key: 'gh10z9DFZ009876E342dfsfd34234dszSD65XRV5',
                        image:$( '.twimg' ).find( 'img' ).attr( 'src' ) 
                    }, 
                    function( data ) { 
                        return data.upload.links.original; 
                    });
            });
    });
}

var tweets = [], i = 0;

/* code to GET twitter page using "request" goes here similar to rehostImage */

/* same jsdom boilerplate as in rehostImage function goes here to get 
   the jQuery handle on the page DOM. Once we get "$"... */

$( '.tweet-text' ).each( function() { 
    i++;
    tweets[ i ] = $( this ).html();
    if( tweets[i].match( t_pic ) ) { tweets[i].replace( t_pic, rehostImage ); }
});

What the code tries to do is simple:

  1. GETs my twitter page
  2. Parses each tweet for any pic.twitter.com links
  3. GETs that page, traverses the DOM, finds the image URL and uploads to imgur via their anon API

The problem is the regex replace here tweets[i].replace( t_pic, rehostImage ). replace takes a function as a second param whose return value is used to replace the matched string; in this case the final imgur URL after upload, data.upload.links.original, replaces the pic.twitter.com URL in my tweet which I then save to my local disk.

How do I get rehostImage to return data.upload.links.original back to the regex replace, when it's all happening in async through callbacks? The usual approach is to take the final operation, the regex replace, and pass it as callback that can be run after data.upload.links.original but since the regex replace happens inline I don't see how I can apply that approach here.

Any pointers?

vjk2005
  • 678
  • 1
  • 12
  • 23

1 Answers1

1

If you want to defer execution of your replace until the async calls return, you could implement this using $.deferred.

This is an example of how you would use it, you'd probably need to tweak it to suit your specific behavior.

First, you need a deferred object:

var deferred = $.Deferred();

Then your async function needs to return a promise:

function rehostImage( match ) {
    // snip
    return deferred.promise();
}

Your replace callback would wait on the deferred to resolve:

tweets[i].replace(t_pic, function () {
    rehostImage().then(function (data) {
        // the callback you want to execute when the deferred resolves
    });
});

And finally your async function needs to resolve the deferred:

function( data ) {
    deferred.resolve(data.upload.links.original); 
});
jbabey
  • 45,965
  • 12
  • 71
  • 94
  • couldn't wrap my head around Promises/Deferreds and my initial attempts today failed miserably, so I couldn't accept your answer; but I'll upvote it as it seems like someone who has a good handle on the concept can make it work along the lines of the code you've posted above. – vjk2005 Aug 07 '12 at 12:02
  • @vjk2005 there are some pretty good [walkthroughs](http://joseoncode.com/2011/09/26/a-walkthrough-jquery-deferred-and-promise/) on how to use it. in a nutshell, it's a way to give callbacks to any code you want. – jbabey Aug 07 '12 at 12:26
  • thanks for that wonderful walkthrough. But it looks like my code had other problems besides Deferreds. I went through the code and rebuilt it piece-by-piece from scratch to make sure the problem was with the Deferreds code I was using and I found my imgur upload code was failing before it even got to the Deferreds part. I posted a question about it here: http://stackoverflow.com/questions/11849894/imgur-uploads-working-via-client-side-javascript-but-not-node-js-flummoxed – vjk2005 Aug 07 '12 at 16:12
  • tiny update: the imgur upload issue I was having is resolved so I'm back on track working on Deferreds; the minute I solve it, I'll accept your answer. Thanks again for that walkthrough. – vjk2005 Aug 07 '12 at 16:41