Windows 8 adventures: the async pattern in JavaScript
After a small detour into ASP.NET MVC, I’m back into my “Windows 8 application development study”. One of the things you’ll notice when writing Windows 8 Metro applications is that most APIs don’t expose any synchronous methods. If an operation takes more than 15ms to be executed, then you’ll be getting an asynchronous call. And notice that asynchronous is not an option here! It’s simply the only way to go.
If you’re developing Windows 8 Metro apps with JavaScript, then it won’t be long until you meet the WinJS.Promise object (which I’ll be calling Promise from now on). Most (if not all) of the asynchronous method you’ll be using end up returning a Promise. For instance, if you’re trying to create a new file, you’ll probably be using the createFileAsync method. This method returns a Promise which you can then use for being called back when that operation is performed. To illustrate this point, lets see some code which creates a new file inside a new folder. Here’s our first try:
<span style="background: #22282a; color: #93c763;">function </span><span style="background: #22282a; color: #f1f2f3;">createFolderAndFile</span><span style="background: #22282a; color: teal;">() { </span><span style="background: #22282a; color: #66747b;">
//create new folder inside temp folder </span><span style="background: #22282a; color: #93c763;">
var </span><span style="background: #22282a; color: #f1f2f3;">tempFolder </span><span style="background: #22282a; color: teal;">= </span><span style="background: #22282a; color: #f1f2f3;">Windows</span><span style="background: #22282a; color: teal;">.</span><span style="background: #22282a; color: #f1f2f3;">Storage</span><span style="background: #22282a; color: teal;">.</span><span style="background: #22282a; color: #f1f2f3;">ApplicationData</span><span style="background: #22282a; color: teal;">.</span><span style="background: #22282a; color: #f1f2f3;">current</span><span style="background: #22282a; color: teal;">.</span><span style="background: #22282a; color: #f1f2f3;">temporaryFolder</span><span style="background: #22282a; color: teal;">;
</span><span style="background: #22282a; color: #f1f2f3;"> tempFolder</span><span style="background: #22282a; color: teal;">.</span><span style="background: #22282a; color: #f1f2f3;">createFolderAsync</span><span style="background: #22282a; color: teal;">(</span><span style="background: #22282a; color: #ec7600;">"testFolder"</span><span style="background: #22282a; color: teal;">, </span><span style="background: #22282a; color: #f1f2f3;">
Windows</span><span style="background: #22282a; color: teal;">.</span><span style="background: #22282a; color: #f1f2f3;">Storage</span><span style="background: #22282a; color: teal;">.</span><span style="background: #22282a; color: #f1f2f3;">CreationCollisionOption</span><span style="background: #22282a; color: teal;">.</span><span style="background: #22282a; color: #f1f2f3;">openIfExists</span><span style="background: #22282a; color: teal;">)
.</span><span style="background: #22282a; color: #f1f2f3;">then</span><span style="background: #22282a; color: teal;">(</span><span style="background: #22282a; color: #93c763;">function </span><span style="background: #22282a; color: teal;">(</span><span style="background: #22282a; color: #f1f2f3;">folder</span><span style="background: #22282a; color: teal;">) {
</span><span style="background: #22282a; color: #f1f2f3;"> folder</span><span style="background: #22282a; color: teal;">.</span><span style="background: #22282a; color: #f1f2f3;">createFileAsync</span><span style="background: #22282a; color: teal;">(</span><span style="background: #22282a; color: #ec7600;">"testFile.txt"</span><span style="background: #22282a; color: teal;">)
.</span><span style="background: #22282a; color: #f1f2f3;">then</span><span style="background: #22282a; color: teal;">(</span><span style="background: #22282a; color: #93c763;">function </span><span style="background: #22282a; color: teal;">(</span><span style="background: #22282a; color: #f1f2f3;">file</span><span style="background: #22282a; color: teal;">) {
</span><span style="background: #22282a; color: #f1f2f3;"> file</span><span style="background: #22282a; color: teal;">.</span><span style="background: #22282a; color: #f1f2f3;">openAsync</span><span style="background: #22282a; color: teal;">( </span><span style="background: #22282a; color: #f1f2f3;">Windows</span><span style="background: #22282a; color: teal;">.</span><span style="background: #22282a; color: #f1f2f3;">Storage</span><span style="background: #22282a; color: teal;">.</span><span style="background: #22282a; color: #f1f2f3;">FileAccessMode</span><span style="background: #22282a; color: teal;">.</span><span style="background: #22282a; color: #f1f2f3;">readWrite</span><span style="background: #22282a; color: teal;">)
.</span><span style="background: #22282a; color: #f1f2f3;">then</span><span style="background: #22282a; color: teal;">(</span><span style="background: #22282a; color: #93c763;">function </span><span style="background: #22282a; color: teal;">(</span><span style="background: #22282a; color: #f1f2f3;">dataStream</span><span style="background: #22282a; color: teal;">) { </span><span style="background: #22282a; color: #93c763;">
var </span><span style="background: #22282a; color: #f1f2f3;">stream </span><span style="background: #22282a; color: teal;">= </span><span style="background: #22282a; color: #f1f2f3;">dataStream</span><span style="background: #22282a; color: teal;">.</span><span style="background: #22282a; color: #f1f2f3;">getOutputStreamAt</span><span style="background: #22282a; color: teal;">(</span><span style="background: #22282a; color: #ffcd22;">0</span><span style="background: #22282a; color: teal;">); </span><span style="background: #22282a; color: #93c763;">
var </span><span style="background: #22282a; color: #f1f2f3;">writer </span><span style="background: #22282a; color: teal;">= </span><span style="background: #22282a; color: #93c763;">new </span><span style="background: #22282a; color: #f1f2f3;">Windows</span><span style="background: #22282a; color: teal;">.</span><span style="background: #22282a; color: #f1f2f3;">Storage</span><span style="background: #22282a; color: teal;">.</span><span style="background: #22282a; color: #f1f2f3;">Streams</span><span style="background: #22282a; color: teal;">.</span><span style="background: #22282a; color: #f1f2f3;">DataWriter</span><span style="background: #22282a; color: teal;">(</span><span style="background: #22282a; color: #f1f2f3;">stream</span><span style="background: #22282a; color: teal;">);
</span><span style="background: #22282a; color: #f1f2f3;"> writer</span><span style="background: #22282a; color: teal;">.</span><span style="background: #22282a; color: #f1f2f3;">writeString</span><span style="background: #22282a; color: teal;">(</span><span style="background: #22282a; color: #ec7600;">"Howdy, there!"</span><span style="background: #22282a; color: teal;">);
</span><span style="background: #22282a; color: #f1f2f3;"> writer</span><span style="background: #22282a; color: teal;">.</span><span style="background: #22282a; color: #f1f2f3;">storeAsync</span><span style="background: #22282a; color: teal;">()
.</span><span style="background: #22282a; color: #f1f2f3;">then</span><span style="background: #22282a; color: teal;">(</span><span style="background: #22282a; color: #93c763;">function </span><span style="background: #22282a; color: teal;">() {
</span><span style="background: #22282a; color: #66747b;"> //don't do anything in callbacks </span><span style="background: #22282a; color: #f1f2f3;">
stream</span><span style="background: #22282a; color: teal;">.</span><span style="background: #22282a; color: #f1f2f3;">flushAsync</span><span style="background: #22282a; color: teal;">()
.</span><span style="background: #22282a; color: #f1f2f3;">then</span><span style="background: #22282a; color: teal;">( </span><span style="background: #22282a; color: #93c763;">function </span><span style="background: #22282a; color: teal;">() { }, </span><span style="background: #22282a; color: #93c763;">function </span><span style="background: #22282a; color: teal;">(</span><span style="background: #22282a; color: #f1f2f3;">err</span><span style="background: #22282a; color: teal;">) { </span><span style="background: #22282a; color: #93c763;">var </span><span style="background: #22282a; color: #f1f2f3;">r </span><span style="background: #22282a; color: teal;">= </span><span style="background: #22282a; color: #ec7600;">""</span><span style="background: #22282a; color: teal;">; });
});
});
});
});
}</span>Wow!!! I’ve written the code and I’m already lost here! Fortunately, you can improve this code by returning promises from your .then methods. By doing that, you can write code which is a little bit more linear. Here’s our initial example rewritten:
<span style="background: #22282a; color: #93c763;">function </span><span style="background: #22282a; color: #f1f2f3;">createFolderAndFile</span><span style="background: #22282a; color: teal;">() { </span><span style="background: #22282a; color: #66747b;">//create new folder inside temp folder </span><span style="background: #22282a; color: #93c763;">var </span><span style="background: #22282a; color: #f1f2f3;">tempFolder </span><span style="background: #22282a; color: teal;">= </span><span style="background: #22282a; color: #f1f2f3;">Windows</span><span style="background: #22282a; color: teal;">.</span><span style="background: #22282a; color: #f1f2f3;">Storage</span><span style="background: #22282a; color: teal;">.</span><span style="background: #22282a; color: #f1f2f3;">ApplicationData</span><span style="background: #22282a; color: teal;">.</span><span style="background: #22282a; color: #f1f2f3;">current</span><span style="background: #22282a; color: teal;">.</span><span style="background: #22282a; color: #f1f2f3;">temporaryFolder</span><span style="background: #22282a; color: teal;">; </span><span style="background: #22282a; color: #f1f2f3;">tempFolder</span><span style="background: #22282a; color: teal;">.</span><span style="background: #22282a; color: #f1f2f3;">createFolderAsync</span><span style="background: #22282a; color: teal;">(</span><span style="background: #22282a; color: #ec7600;">"testFolder"</span><span style="background: #22282a; color: teal;">, </span><span style="background: #22282a; color: #f1f2f3;">Windows</span><span style="background: #22282a; color: teal;">.</span><span style="background: #22282a; color: #f1f2f3;">Storage</span><span style="background: #22282a; color: teal;">.</span><span style="background: #22282a; color: #f1f2f3;">CreationCollisionOption</span><span style="background: #22282a; color: teal;">.</span><span style="background: #22282a; color: #f1f2f3;">openIfExists</span><span style="background: #22282a; color: teal;">) .</span><span style="background: #22282a; color: #f1f2f3;">then</span><span style="background: #22282a; color: teal;">(</span><span style="background: #22282a; color: #93c763;">function </span><span style="background: #22282a; color: teal;">(</span><span style="background: #22282a; color: #f1f2f3;">folder</span><span style="background: #22282a; color: teal;">) { </span><span style="background: #22282a; color: #93c763;">return </span><span style="background: #22282a; color: #f1f2f3;">folder</span><span style="background: #22282a; color: teal;">.</span><span style="background: #22282a; color: #f1f2f3;">createFileAsync</span><span style="background: #22282a; color: teal;">(</span><span style="background: #22282a; color: #ec7600;">"testFile.txt"</span><span style="background: #22282a; color: teal;">) }) .</span><span style="background: #22282a; color: #f1f2f3;">then</span><span style="background: #22282a; color: teal;">( </span><span style="background: #22282a; color: #93c763;">function </span><span style="background: #22282a; color: teal;">(</span><span style="background: #22282a; color: #f1f2f3;">file</span><span style="background: #22282a; color: teal;">) { </span><span style="background: #22282a; color: #93c763;">return </span><span style="background: #22282a; color: #f1f2f3;">file</span><span style="background: #22282a; color: teal;">.</span><span style="background: #22282a; color: #f1f2f3;">openAsync</span><span style="background: #22282a; color: teal;">( </span><span style="background: #22282a; color: #f1f2f3;">Windows</span><span style="background: #22282a; color: teal;">.</span><span style="background: #22282a; color: #f1f2f3;">Storage</span><span style="background: #22282a; color: teal;">.</span><span style="background: #22282a; color: #f1f2f3;">FileAccessMode</span><span style="background: #22282a; color: teal;">.</span><span style="background: #22282a; color: #f1f2f3;">readWrite</span><span style="background: #22282a; color: teal;">); }) .</span><span style="background: #22282a; color: #f1f2f3;">then</span><span style="background: #22282a; color: teal;">( </span><span style="background: #22282a; color: #93c763;">function </span><span style="background: #22282a; color: teal;">(</span><span style="background: #22282a; color: #f1f2f3;">dataStream</span><span style="background: #22282a; color: teal;">) { </span><span style="background: #22282a; color: #93c763;">var </span><span style="background: #22282a; color: #f1f2f3;">stream </span><span style="background: #22282a; color: teal;">= </span><span style="background: #22282a; color: #f1f2f3;">dataStream</span><span style="background: #22282a; color: teal;">.</span><span style="background: #22282a; color: #f1f2f3;">getOutputStreamAt</span><span style="background: #22282a; color: teal;">(</span><span style="background: #22282a; color: #ffcd22;">0</span><span style="background: #22282a; color: teal;">); </span><span style="background: #22282a; color: #93c763;">var </span><span style="background: #22282a; color: #f1f2f3;">writer </span><span style="background: #22282a; color: teal;">= </span><span style="background: #22282a; color: #93c763;">new </span><span style="background: #22282a; color: #f1f2f3;">Windows</span><span style="background: #22282a; color: teal;">.</span><span style="background: #22282a; color: #f1f2f3;">Storage</span><span style="background: #22282a; color: teal;">.</span><span style="background: #22282a; color: #f1f2f3;">Streams</span><span style="background: #22282a; color: teal;">.</span><span style="background: #22282a; color: #f1f2f3;">DataWriter</span><span style="background: #22282a; color: teal;">(</span><span style="background: #22282a; color: #f1f2f3;">stream</span><span style="background: #22282a; color: teal;">); </span><span style="background: #22282a; color: #f1f2f3;">writer</span><span style="background: #22282a; color: teal;">.</span><span style="background: #22282a; color: #f1f2f3;">writeString</span><span style="background: #22282a; color: teal;">(</span><span style="background: #22282a; color: #ec7600;">"Howdy, there!"</span><span style="background: #22282a; color: teal;">); </span><span style="background: #22282a; color: #93c763;">return </span><span style="background: #22282a; color: #f1f2f3;">writer</span><span style="background: #22282a; color: teal;">.</span><span style="background: #22282a; color: #f1f2f3;">storeAsync</span><span style="background: #22282a; color: teal;">() .</span><span style="background: #22282a; color: #f1f2f3;">then</span><span style="background: #22282a; color: teal;">(</span><span style="background: #22282a; color: #93c763;">function </span><span style="background: #22282a; color: teal;">() { </span><span style="background: #22282a; color: #f1f2f3;">stream</span><span style="background: #22282a; color: teal;">.</span><span style="background: #22282a; color: #f1f2f3;">flushAsync</span><span style="background: #22282a; color: teal;">() .</span><span style="background: #22282a; color: #f1f2f3;">then</span><span style="background: #22282a; color: teal;">(</span><span style="background: #22282a; color: #93c763;">function </span><span style="background: #22282a; color: teal;">() {},</span><span style="background: #22282a; color: #93c763;">function </span><span style="background: #22282a; color: teal;">() {}); }); }); }</span>I’d say that this is better (at least, from a readability point of view). What do you think?
Btw, I’m almost positive that you’ve notice the empty then calls on the flishAsync method? Well, I’m still not sure why, but the thing is that “empty” call is needed in order for the bytes to be saved to the file. If you remove the .then call, you’ll end up with an empty file (no, are we talking about a bug here?? )
And that’s it for now. Stay tuned for more.
Post Footer automatically generated by Add Post Footer Plugin for wordpress.
