When programming in JavaScript (or CoffeeScript) you sometimes face a
situation where you need to complete multiple independant asynchronous
methods before continuing. This situation crops up in web browsers but
its much more common when writing server-side JavaScript, e.g. with
Node.js - for example you might need to fetch from a
database, fetch from a KVS,
read a file and perform a remote HTTP request before outputting the
compiled information to the end user.
One method of solving this issue is to chain the asynchronous calls, however this
means that they’re run one after the other (serially) - and thus it will
take longer to complete them. A better way would be to run them in
parallel and have something track their completion. This is exactly what
my very simple AsyncBatch class does:
.coffee.jsAsyncBatch class, triggers event once all wrapped callbacks complete
###Create a new AsyncBatch instance###batch = newAsyncBatch###Wrap your callbacks with batch.wrap and a nameThe name is used to store the result returned by your callback(Don't forget to return the result you're interested in!)###delay50,batch.wrap'timer',->return"Timer complete"###Add a completion handler that accepts `results` as a parameter. `results` is a JS object where the keys are the callback names from above and the values are the return values of the callbacks.###batch.on'done',(results) ->console.log"Batch complete, timer result: #{results.timer}"
12345678910111213141516171819202122232425262728
/*Create a new AsyncBatch instance*/varbatch;batch=newAsyncBatch;/*Wrap your callbacks with batch.wrap and a nameThe name is used to store the result returned by your callback(Don't forget to return the result you're interested in!)*/delay(50,batch.wrap('timer',function(){return"Timer complete";}));/*Add a completion handler that accepts `results` as a parameter. `results` is a JS object where the keys are the callback names from above and the values are the return values of the callbacks.*/batch.on('done',function(results){returnconsole.log("Batch complete, timer result: "+results.timer);});
NOTE: If your asynchronous method accepts both a success and
failure callback then simply wrap both individually but
ensure you use the same name for both.
NOTE: Other than the case in the previous NOTE, all callbacks
should have different names.
A full example (including a stub EventEmitter implementation) follows:
I really like CoffeeScript. I really like JavaScript. I’m trying to get
into the habit of writing more CoffeeScript since, I think, it allows
me to express myself clearer and more concisely, leading to shorter
code and (hopefully) less mistakes/bugs. It also makes jumping back into
code a month later a much simpler affair since reading the code is so
much easier.
The following JavaScript creates a new closure inside each iteration of
a loop to store the value of the loop variable i into the closure’s
local variable j so that we don’t just console.log(10) 10 times.
Though this example is trivial it represents a common method for
solving closure related issues in asynchronous code.
The following CoffeeScript does exactly the same thing, you can see the
translation line by line from the JavaScript. However once you’ve learnt
the syntax you can much more clearly see what is going on with the
CoffeeScript than in the verbose and parenthesis-heavy JavaScript above.
Strict guidelines will also be placed on internet providers which offer so-called unlimited packages, where users can download to their heart’s content. If a user incurs an “additional charge or suspension of service as a consequence of exceeding a usage threshold,” the ISP cannot use the term “unlimited”.
Finally! I hate it when providers are allowed to say ‘Unlimited’ and then place a limit on it (generally via a Fair Usage Policy). What a load of rubbish! I’m a heavy bandwidth user so it makes it very hard for me to find out what the limit is and then choose the relevant provider. Fortunately Virgin’s XXL broadband truly is unlimited - at least for now!
I was having an issue with Disqus - my comments were showing on post pages, but the comment count was not. Turns out that this is a simple to fix error relating to JavaScript scope.
Scope issue
The developer of Octopress has kindly protected our window object from being polluted with various unnecessary variables by wrapping the whole disqus include in an anonymous function.
However, the Disqus script actually injects another <script> element into the <head> (or <body>) of the page. This injected script, like all other scripts on the page, inherits the global (window) scope, and not the scope of this anonymous function. Thus it will not have access to the disqus variables such as disqus_shortname defined within the anonymous function. Without these variables, disqus loads such domains as undefined.disqus.com which is not very helpful!
Solution
The fix is simple - open up source/_includes/disqus.html and move the beginning of the anonymous function ((function () {) so that it does not contain the var disqus_* variable definitions. We also need to insert a couple of semi-colons that have been accidentally omitted. Here’s the resulting file:
Modified disqus include to fix disqus comment count. (disqus.html)download
12345678910111213141516171819202122
{% comment %} Load script if disquss comments are enabled and `page.comments` is either empty (index) or set to true {% endcomment %}
{% if site.disqus_short_name and page.comments != false %}
<script type="text/javascript">vardisqus_shortname='{{ site.disqus_short_name }}';{%ifpage.comments==true%}{%comment%}`page.comments`canbeonlybesettotrueonpages/posts,soweembedthecommentshere.{%endcomment%}// var disqus_developer = 1;vardisqus_identifier='{{ site.url }}{{ page.url }}';vardisqus_url='{{ site.url }}{{ page.url }}';vardisqus_script='embed.js';{%else%}{%comment%}As`page.comments`isempty,wemustbeontheindexpage.{%endcomment%}vardisqus_script='count.js';{%endif%}(function(){vardsq=document.createElement('script');dsq.type='text/javascript';dsq.async=true;dsq.src='http://'+disqus_shortname+'.disqus.com/'+disqus_script;(document.getElementsByTagName('head')[0]||document.getElementsByTagName('body')[0]).appendChild(dsq);}());</script>{% endif %}
One more thing
If you want your links to be absolute, then you should set root to be your root URL in _config.yml - e.g. root: http://www.benjiegillam.com/ rather than root: /. This may or may not be required for Disqus to function properly.
EDIT: @Octopress has confirmed that root must not contain the scheme and host.
Bonus
Now that I’ve implemented the above, my comments and comment counts successfully display on my benjiegillam.dev site (hosted by Pow)!
Annoyingly, Mac OS X’s sed is not 100% compatible with GNU sed, and so this solution from StackOverflow did not immediately solve my issue:
Or use this solution with sed: sed ':a;N;$!ba;s/\n/ /g' This will read the whole file in a loop, then replaces the newline(s) with a space. Update: explanation.
create a register via :a
append the current and next line to the register via N
if we are before the last line, branch to the created register $!ba ($! means not to do it on the last line (as there should be one final newline)).
finally the substitution replaces every newline with a space on the pattern space (which is the contents of the a register = the whole file.
The solution I found was to split the command up into explicit separate commands:
Replacing newlines with spaces using sed on Mac OS X
After running exitwp to import my blog, I ran rake generate to build it, and got the following issue:
$ rake generate
(in /Users/benjiegillam/Documents/Blog/octopress)
## Generating Site with Jekyll
Configuration from /Users/benjiegillam/Documents/Blog/octopress/_config.yml
unchanged sass/screen.scss
Building site: source -> public
/Users/benjiegillam/Documents/Blog/octopress/plugins/raw.rb:11:in `gsub': invalid byte sequence in UTF-8 (ArgumentError)
from /Users/benjiegillam/Documents/Blog/octopress/plugins/raw.rb:11:in `unwrap'
[...]
However, converting the file was perfectly valid UTF-8 as confirmed by an iconv -c conversion followed by a diff -u.
After a quick bit of hacking in the octopress/plugins/raw.rb file to spit out the content that was being converted, I found the file at fault. After some iteration I got to the root of the issue - Octopress’ default markdown parser, rdiscount, REALLY doesn’t like UTF-8 characters in URLs. I’ve built a test here:
I had a few issues going from Wordpress to Octopress (which I did using exitwp) - I’ve outlined
some of my solutions below.
Crossed out code blocks
The markdown produced by pandoc (an exitwp dependency) uses ~~~~ in places to denote a code block. However, rdiscount (Octopress’ markdown parser) recognises this instead as strike-through. A simple bash command fixes this (though it does lose some of the formatting details).
Fix tildes generated by pandoc (exitwptildes.sh)download
12345
for I in *; do sed -i '' -e 's/^~~~~ {\(.*\)}$/\<!-- \1 -->\```/g'$I; donefor I in *; do sed -i '' -e 's/^~~~~$/\```/g'$I; done
Line breaks in links
For some reason exitwp added some linebreaks into the markup for the links. And for some other
reason, octopress’ style sheet (screen.css) tells <a> tags that their white-space should be
pre-wrap, these two issues combine to give you broken links.
This next command should fix these newline issues.
If you’re trying to access a session from a socket.io socket, I
recommend following
these instructions which work
great for MemoryStore. However for RedisStore you need to do a
little more work because the object returned is just a JSON object - not
a Session object as it should be. I’ve hacked around it by adding the
following (CoffeeScript) to convert it to a Session object if
required:
If you’re using nodemon to automatically restart your node/coffee
server process when the source code changes (and if you’re not, why
not?), you may find (as I did) that it can kill the script quite
abruptly. If you want to do something when the code shuts down (close
connections, flush to database/disk/redis/memcached, etc) then you can
do so by intercepting the SIGUSR2 signal that nodemon uses to kill
your script, and then setting up these actions. Once these actions are
complete, you can exit but nodemon will see that as a crash and stop
restarting. So you then have to kill your app again with SIGUSR2.
Here’s the solution I use in CoffeeScript (click Toggle for JavaScript):
.coffee.jsCleanly restart on nodemon SIGUSR2 (.coffee)
123456789101112
# Alias for setTimeout that reorders parameters to look neater in CoffeeScriptdelay = (ms, cb) ->setTimeoutcb,ms# Handle SIGUSR2, but only onceprocess.once'SIGUSR2',->console.log'Doing shutdown tasks...'delay5000,->console.log'All done, exiting'# Kill ourself with the SIGUSR2 signal againprocess.killprocess.pid,'SIGUSR2'# This next line just to prevent the app exiting in case you want to use this as a demodelay99999999,->console.log'App exiting naturally'