A Promise is a programming model that deals with deferred results in concurrent programming. The basic idea around promises are that rather than issuing a blocking call for a resource (IO, Network, etc.) you can immediately return a promise for a future value that will eventually be fulfilled. This allows you to write non-blocking logic that executes asynchronously without having to write a lot of synchronization and plumbing code.
For example, lets say we want to load an RSS feed via HTTP. To accomplish this in JavaScript we have to leverage the XMLHttpRequest object. The following example shows how one might load the RSS feed from this blog using conventional means:
1: function fetchSync(url) { 2: var xhr = new XMLHttpRequest();
3: xhr.open(url, "GET", false, null, null);
4: xhr.send(null);
5: if (xhr.status == 200) { 6: return xhr;
7: }
8: throw new Error(xhr.statusText);
9: }
10:
11: function fetchAsync(url, callback, errorCallback) { 12: var xhr = new XMLHttpRequest();
13: xhr.onreadystatechange = function() { 14: if (xhr.readyState == 4) { 15: if (xhr.status == 200) { 16: callback(xhr);
17: }
18: else { 19: errorCallback(new Error(xhr.statusText));
20: }
21: }
22: }
23: xhr.open(url, "GET", false, null, null);
24: xhr.send(null);
25: }
In the first example, the RSS feed is fetched synchronously (see line 3). In this case the code is straightforward but the request is blocking, which means script execution is suspended until the request is completed or fails. This is not optimal in an AJAX application as it can impact the interactivity of the page.
The second example attempts to alleviate the blocking call by introducing a callback that will be executed when the request completes. This is advantageous as it is non-blocking, however the programming model can become inconsistent if you are working with different asynchronous data sources including loading local resources, performing computationally intensive tasks (such as xml transforms or other data transformation), etc.
To actually use the resulting feed you might have to do some additional work:
1: fetchAsync(
2: "http://blogs.msdn.com/rbuckton/rss.xml",
3: function (rss) { 4: fetchAsync(
5: "/some/stylesheet.xsl",
6: function (xsl) { 7: document.getElementById("host").innerHTML = rss.responseXML.transformNode(xsl.documentElement); 8: },
9: function (e) { 10: document.getElementById("host").innerHTML = "Error: " + ex.message; 11: })
12: },
13: function (ex) { 14: document.getElementById("host").innerHTML = "Error: " + ex.message; 15: });
In this example we now have two asynchronous calls for content, however one depends on the other and as a result we can’t take advantage of concurrent requests to speed up the process.
Now lets look at a concurrent example:
1: var rss = null;
2: var xsl = null;
3:
4: function tryComplete() { 5: if (rss != null && xsl != null) { 6: document.getElementById("host").innerHTML = rss.responseXML.transformNode(xsl.documentElement); 7: }
8: }
9:
10: fetchAsync(
11: "http://blogs.msdn.com/rbuckton/rss.xml",
12: function(rssXhr) { 13: rss = rssXhr;
14: tryComplete();
15: },
16: function (e) { 17: document.getElementById("host").innerHTML += "Error: " + e.message + "<br />"; 18: });
19:
20: fetchAsync(
21: "/some/stylesheet.xsl",
22: function (xslXhr) { 23: xsl = xslXhr;
24: tryComplete();
25: },
26: function (e) { 27: document.getElementById("host").innerHTML += "Error: " + e.message + "<br />"; 28: });
Now the above example can utilize concurrent requests and synchronize the results, but wow that’s a lot code.
Using promises you can instead do the following:
1: var prss = Promise.fetch("http://blogs.msdn.com/rbuckton/rss.xml"); 2: var pxsl = Promise.fetch("/some/stylesheet.xsl"); 3: Promise.whenOnly(
4: Promise.join(prss, pxsl),
5: function() { 6: var rss = prss.get_value();
7: var xsl = prss.get_value();
8: document.getElementById("host").innerHTML = rss.responseXML.transformNode(xsl.responseXML); 9: },
10: function(ex) { 11: document.getElementById("host").innerHTML = "Error: " + ex.message; 12: });
Another feature of Promises is pipelining, the ability to create a series of operations based on the result of promises. The Promise.when() function is used to pipeline a promise, creating a new Promise based on the eventual output of the source. For example:
1: var pfeed = Promise.fetch("somefeed.xml"); 2:
3: var pfeedXml = Promise.when(pfeed, function(feed) { return feed.responseXML; }); 4: Promise.whenOnly(pfeedXml, function(feedXml) { alert(feedXml.xml); }); 5:
6: var pfeedText = Promise.when(pfeed, function(feed) { return feed.responseText; }); 7: Promise.whenOnly(pfeedText, function(feedText) { console.log(feedText); });
On line 1 we fetch an xml document from the server. On line 3 we create a pipeline that will eventually convert the response into an XML document. On line 4 when the document is finally available we can alert the user. On line 6 we create a pipeline that will eventually get the response value as text. On line 7 we can log it to the console.
Here’s an example of using pipelined Promises to get the public twitter timeline in JSONP:
1: Promise
2: .jsonp("http://twitter.com/statuses/public_timeline.json") 3: .whenOnly(function(timeline) { 4: alert(timeline.length);
5: });
A promise can be created for other deferred actions as well. For example:
1: var pinput = new Promise(
2: function(fulfillPromise, breakPromise) { 3: var btn = document.getElementById("button"); 4: var txt = document.getElementById("txt"); 5: btn.onclick = function() { 6: fulfillPromise(txt.value);
7: }
8: }
9: );
10:
11: pinput.whenOnly(function(value) { alert(value); });
The above sample creates a promise for eventual input that you can act on at any point.
Whether a promise completes after a delay or very quickly (even synchronously), the programming model doesn’t change. The Promise model can even handle present information, so that present and future data can be mixed within the model. This is due to Promise.immediate(), which creates a promise that is fulfilled synchronously. All of the “static” methods on the Promise object will implicitly create a Promise for any non-future data (data that is not already encapsulated in a Promise).
Here’s an example of a future factorial (based on the Wikipedia example):
1: function factorial(value) { 2: return new Promise(
3: function(fulfillPromise, breakPromise) { 4: setTimeout(
5: function() { 6: var result = 0;
7: do { 8: result += value;
9: }
10: while (--value > 0);
11: fulfillPromise(result);
12: },
13: 1);
14: }
15: );
16: }
17:
18: var pfactorial = factorial(100000)
19: .when(function(result) { return result + 3; }) 20: .whenOnly(function(result) { alert(result); });
This example uses setTimeout (at line 4) to ensure the request returns immediately and does not block linear execution of the code.
*NOTE: The attached API does not overcome the Same Origin Policy when used in the browser. This means that you can only make requests back to the same domain as the caller. Same Origin Policy does not apply for Gadgets (Windows 7 and Windows Vista) or for Widgets (Windows Mobile 6.5), which is where I’ve used this library for the most part. I have added some JSONP support in the library however JSONP is very limited.
Here is a quick overview of the current Promise API:
// constructor
var p = new Promise(initCallback)
// initCallback: callback function that takes two
// parameters, the fulfillPromise and breakPromise
// callback functions.
// members
p.get_isComplete()
// Gets whether the promise is complete
// returns: Boolean value that is True if the promise is
// complete
p.get_value()
// Gets the value of the promise or 'undefined' if the
// promise has not completed. If the promise was
// broken during execution an exception will be thrown
// returns: Object value
p.call(name, params)
// Calls a named function on the future value of the
// promise
// name: String value for name of the field on the
// promise result object that contains a function
// to call
// params: parameter array of arguments to the
// function. specified as in-line arguments not as an
// array: p.call("foo", 1, 2, 3)
// returns: a promise for the result of the call
p.getValue(name)
// Gets the value of a named field on the future
// value of the promise
// name: String value for the name of a field on the
// promise result object
// returns: a promise for the value of the field
p.setValue(name, value)
// Sets the value of a named field on the future
// value of the promise
// name: String value for the name of a field on the
// promise result object
// value: Object value to set on the field for the
// promise result object
// returns: none
p.when(fulfillPromise, breakPromise, context)
// Creates a promise for the value of an action taken
// on the future value of the promise
// fulfillPromise: Function that takes the value of the
// promise when it completes and is used to act
// on the future value. The result is used as the
// value of the new promise
// breakPromise: Function that takes the exception
// raised by the promise when it fails and is used
// to act on the failure. The result is used as the
// value of the new promise
// context: Optional Object to use as the 'this' value
// for the fulfillPromise or breakPromise callbacks
// returns: a Promise for the result of actions taken
// in fulfillPromise or breakPromise
p.whenOnly(fulfillPromise, breakPromise, context)
// Enqueues an action to be taken on the future
// value of the promise
// fulfillPromise: Function that takes the value of the
// promise when it completes and is used to act
// on the future value.
// breakPromise: Function that takes the exception
// raised by the promise when it fails and is used
// to act on the failure.
// context: Optional object to use as the 'this' value
// for the fulfillPromise or breakPromise callbacks
// returns: none
p.join(promises)
// Creates a new promise that completes when all
// joined promises have completed
// promises: Parameter array of other promises
// returns: a new Promise
// static/type members
Promise.when(promise, fulfillPromise, breakPromise, context)
// Creates a promise for the value of an action taken
// on the future value of the promise
// promise: The promise on which to act
// fulfillPromise: Function that takes the value of the
// promise when it completes and is used to act
// on the future value. The result is used as the
// value of the new promise
// breakPromise: Function that takes the exception
// raised by the promise when it fails and is used
// to act on the failure. The result is used as the
// value of the new promise
// context: Optional Object to use as the 'this' value
// for the fulfillPromise or breakPromise callbacks
// returns: a Promise for the result of actions taken
// in fulfillPromise or breakPromise
Promise.whenOnly(promise, fulfillPromise, breakPromise, context)
// Enqueues an action to be taken on the future
// value of the promise
// promise: The promise on which to act
// fulfillPromise: Function that takes the value of the
// promise when it completes and is used to act
// on the future value.
// breakPromise: Function that takes the exception
// raised by the promise when it fails and is used
// to act on the failure.
// context: Optional object to use as the 'this' value
// for the fulfillPromise or breakPromise callbacks
// returns: none
Promise.join(promises)
// Creates a new promise that completes when all
// joined promises have completed
// promises: Parameter array of other promises
// returns: a new Promise
Promise.make(value)
// Makes a promise on a value. If the value is a promise it
// is returned. If the value is not a promise an immediate
// promise is returned
// value: an Object that may or may not be a promise
// returns: a Promise for the value
Promise.broken(exception)
// Makes a promise that is immediately broken
// exception: The error for the promise
// returns: a broken Promise for the exception
Promise.immediate(value)
// Makes a promise that is immediately fulfilled
// value: The value for the promise
// returns: a fulfilled Promise for the value
Promise.fetch(url, method, username, password, headers, query, body)
// Creates a Promise for a completed XMLHttpRequest for the
// requested resource
// url: String value for the url
// method: Optional string value for the method. default is "GET"
// username: Optional string value for the username for Basic
// Authentication. default is 'null'
// password: Optional string value for the password for Basic
// Authentication. default is 'null'
// headers: Optional Object containing name/value pairs for
// HTTP headers. default is 'null'
// query: Optional Object or String containing name/value pairs
// to be added to the url as a querystring. default is 'null'
// body: Optional Object or String containing the request
// body for the request. A String is sent as-is while an Object
// contains name/value pairs that are url form encoded.
// default is null
// returns: A Promise for the completed XMLHttpRequest
Promise.json(url, method, username, password, headers, query, body)
// Creates a Promise for a JSON object parsed from the result of an
// XMLHttpRequest for the requested resource.
// This requires the JSON object which is Native in IE8. If you
// wish to use this method on a browser that does not have the
// native JSON object, please download www.json.org/json2.js
// url: String value for the url
// method: Optional string value for the method. default is "GET"
// username: Optional string value for the username for Basic
// Authentication. default is 'null'
// password: Optional string value for the password for Basic
// Authentication. default is 'null'
// headers: Optional Object containing name/value pairs for
// HTTP headers. default is 'null'
// query: Optional Object or String containing name/value pairs
// to be added to the url as a querystring. default is 'null'
// body: Optional Object or String containing the request
// body for the request. A String is sent as-is while an Object
// contains name/value pairs that are url form encoded.
// default is null
// returns: A Promise for the JSON value of the response
Promise.jsonp(url, query, callbackArg)
// Uses JSONP to request a JSON resource
// url: String value for the url
// query: Optional Object or String containing name/value pairs
// to be added to the url as a querystring. default is 'null'
// callbackArg: Optional string that specifies the name of
// a querystring argument used to hold the name of
// a callback function. default is "callback"
Promise.js is licensed under the MSR-LA for non-commercial use only. The license is included in license.txt in the zip.
The Team Explorer in Visual Studio provides a quick (and not exactly obvious) method for attaching a screenshot or other content to a Work Item.
Screenshots
To quickly add a screenshot do the following:
1. Press PrtScrn or Alt+PrntScrn (Captures only the currently active window)
2. Open a Work Item (Bug, Task, etc) in team explorer
3. Click “File Attachments”
4. Press Ctrl+V (Paste)
A file called “Screenshot.png” is automatically attached to the work item.
Copied Text
To quickly add any copied text as an attachment to a work item do the following:
1. Select the text to copy (or press Ctrl+A to select all text in the active input area like a page or document)
2. Open a Work Item (Bug, Task, etc) in team explorer
3. Click “File Attachments”
4. Press Ctrl+V (Paste)
A file called “PastedText.txt” is automatically attached to the work item.
Copied Files
You can also quickly add a file from your desktop as an attachment using the same steps as above:
1. Select the files to copy
2. Open a Work Item (Bug, Task, etc) in team explorer
3. Click “File Attachments”
4. Press Ctrl+V (Paste)
Each of the files selected is now attached to the work item.
Bonus Tip – Copy and Paste Text from an Alert
If you get an alert in a page like so:

Simply press Ctrl+C (Copy) to copy the contents of the dialog into the clipboard. To paste the text you can just use Ctrl+V:
---------------------------
Windows Internet Explorer
---------------------------
You can copy this
---------------------------
OK
---------------------------
I've been working on some bugs in the Calendar extender from the ASP.NET AJAX Control Toolkit and have been resolving some of the focus/blur events. I've been trying to make the calendar work consistently across all of the browsers and found some frustrating differences in event firing order between the browsers.
Here are a list of some of the DOM events supported by the various browsers I tested:
Table 1 - DOM Events by Browser
| Event | DOM Level | IE | FF | OP | SF |
| DOMActivate | Level 2 | | | | * |
| activate | 2 | * | | | |
| DOMFocusIn | Level 2 | | | 1 | 1 |
| focusin | 2 | * | | | |
| focus | Level 0 | * | 1 | 1 | 1 |
| DOMDeactivate | Level 2 | | | | |
| deactivate | 2 | * | | | |
| DOMFocusOut | Level 2 | | | 1 | 1 |
| focusout | 2 | * | | | |
| blur | Level 0 | * | 1 | 1 | 1 |
| mousedown | Level 0 | * | * | * | * |
| mouseup | Level 0 | * | * | * | * |
| click | Level 0 | * | * | * | * |
IE: Internet Explorer 7.0
FF: Firefox 2.0.0.6
OP: Opera 9.23
SF: Safari 3.03 Beta (Windows)
*: Implemented on any element
1: Implemented only on specific elements
2: IE-only implementation
Internet Explorer uses activate/focusin/focusout instead of DOMActivate/DOMFocusIn/DOMFocusOut for events. Interestingly only IE and Safari support DOMActivate/activate on any element. Another thing I saw was that IE is the only browser which fires focus/blur events for any element. Other browsers can support the focus/blur events on any element that has a tab index however.
The next version of Calendar needs to support the following activation scenarios depending on whether it is also associated with a button:
- No Button
- Show Popup when textbox receives focus
- Show Popup when textbox is clicked
- Hide Popup when textbox loses focus
- Hide Popup when textbox receives ESC keypress
- Hide Popup when a date is selected
- Button
- Show Popup when the button is clicked
- Hide Popup when the button loses focus
- Hide Popup when the button receives ESC keypress
- Hide Popup when a date is selected
The issue is that when the textbox or button loses focus the popup should hide unless the focus is being directed at the popup itself. The problem is that only IE recognizes a focus event on the popup's DIV tag when the popup is not part of the tab order (and we don't want the popup in the tab order). I was curious the order of DOM events fired in each browser so I wrote a small test page and stepped through for each. The results are as follows:
Table 2 - DOM Event Order by browser
| IE | FF | OP | SF |
- // click on INPUT
- INPUT: mousedown
- INPUT: activate
- INPUT: focusin
- INPUT: focus
- INPUT: mouseup
- INPUT: click
- // click on DIV
- DIV: mousedown
- INPUT: deactivate
- INPUT: focusout
- DIV: activate
- DIV: focusin
- INPUT: blur
- DIV: focus
- DIV: mouseup
- DIV: click
- // click away
- DIV: deactivate
- DIV: focusout
- DIV: blur
| - // click on INPUT
- INPUT: mousedown
- INPUT: focus
- INPUT: mouseup
- INPUT: click
- // click on DIV
- DIV: mousedown
- INPUT: blur
- DIV: mouseup
- DIV: click
- // click away
| - // click on INPUT
- INPUT: focus
- INPUT: DOMFocusIn
- INPUT: mousedown
- INPUT: mouseup
- INPUT: click
- // click on DIV
- INPUT: blur
- INPUT: DOMFocusOut
- DIV: mousedown
- DIV: mouseup
- DIV: click
- // click away
| - // click on INPUT
- INPUT: mousedown
- INPUT: focus
- INPUT: DOMFocusIn
- INPUT: mouseup
- INPUT: click
- INPUT: DOMActivate
- // click on DIV
- DIV: mousedown
- INPUT: blur
- INPUT: DOMFocusOut
- DIV: mouseup
- DIV: click
- DIV: DOMActivate
- // click away
|
Immediately a few things become obvious. First, I obviously cannot rely on focus/blur on the other browsers. IE would allow me to capture focusin on the DIV before the blur fires on the INPUT but that's just IE. Second, on all browsers except Opera the mousedown event of the DIV fires before the blur event of the INPUT. There's no clean sequential way to handle the focus transition in all browsers.
In the current version of Calendar we're making use of a "threading" class I created for the toolkit called DeferredOperation. DeferredOperation wraps a delegate and executes it asynchronously using setTimout. It has some built-in synchronization semantics and was used to handle the focus changes while the popup DIV was part of the tab order (which again, we don't really want). This introduces a subtle delay between activation requests and is problematic on Safari (we had to wait a full second before processing the deferred operation to let safari to catch up).
What I've opted for now is to do the best I can in three out of four of the browsers. I track mousedown/mouseup using a flag and in Internet Explorer, Firefox, and Safari and monitor the flag in the blur event of the textbox. To handle the Opera inconsistency I again use a DeferredOperation to asynchronously handle the blur event which fixes the issue in that browser.
The final issue I discovered was that in all browsers, clicking on the popup causes the textbox (or button) to lose focus which again causes problems with the tab order. On the new Calendar I reset the focus to the textbox (or button) on mouseup on the popup.
Hopefully these changes should make the next toolkit release so keep an eye out for the next update.
Finally back on the blogging scene. A lot has happened in the last 6 months and now that things are settling down I finally have a chance to start putting some content on my blog.
First, a little about me. My name is Ron Buckton and I am a Senior Consultant for Microsoft Services in the Federal District, Public Sector. What this means is that I work primarily with Public Sector government contracts for MCS. I started with Microsoft back in May after a number of interviews and trips back and forth to Redmond, WA and Washington, DC. Prior to joining Microsoft I was working for a Microsoft partner in Cleveland, OH where I previously did most of my blogging.
Back in 2006 I joined the Ajax Control Toolkit as a contributor and among my contributions number the Calendar, Tabs, DropDown, and a number of other smaller extenders and numerous architectural features within the toolkit such as ScriptControl and ScriptUserControl. I had a number of useful blog posts about the toolkit on my former blog but unfortunately the blog was taken down shortly after I left and I have yet to have the time to pull those posts out of my archives to republish here.
A number of people on the ASP.Net forums have been asking for me to republish the content in some form, specifically the posts revolving around skinning the Calendar and Tabs controls using CSS. I plan on having those posts cleaned up and published as articles on this blog before too much longer.
Aside from the Toolkit and my original ASP.Net development focus I am now very heavily involved in mobile device development. As a result you'll see a fair amount of mobility content in addition to some of my other favorite topics.
Other than this introductory post I'll try to keep the signal very high and the content coming as frequently as I can. Here's what to expect in the coming weeks assuming I can find the time between a heavy workload, family, and other obligations:
- Ajax
- Skinning Calendar and Tabs (redux)
- Upcoming Calendar and Tabs bugfixes and enhancements
- My mile-long wishlist of control/extender ideas after I get my bug-count down
- Ajax unit testing
- Mobile
- Mobile UI wizardry
- BackgroundWorker for Compact Framework
- Mobile unit testing with Visual Studio 2008
- Workflow
- WPF
- Silverlight
If any of this interests you, subscribe and comment. Until then, happy coding.