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:
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?
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);
});