Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>First make sure you're understanding how Promises are supposed to work. Have a look at the <a href="http://wiki.commonjs.org/wiki/Promises" rel="nofollow">CommonJs <em>Promises</em> proposals</a> and the <a href="http://promisesaplus.com/" rel="nofollow">Promises/A+ specification</a> for that.</p> <p>There are two basic concepts that can be implemented each in a few simple lines:</p> <ul> <li><p>A Promise does asynchronously get resolved with the result. Adding callbacks is a transparent action - independent from whether the promise is resolved already or not, they will get called with the result once it is available.</p> <pre><code>function Deferred() { var callbacks = [], // list of callbacks result; // the resolve arguments or undefined until they're available this.resolve = function() { if (result) return; // if already settled, abort result = arguments; // settle the result for (var c;c=callbacks.shift();) // execute stored callbacks c.apply(null, result); }); // create Promise interface with a function to add callbacks: this.promise = new Promise(function add(c) { if (result) // when results are available c.apply(null, result); // call it immediately else callbacks.push(c); // put it on the list to be executed later }); } // just an interface for inheritance function Promise(add) { this.addCallback = add; } </code></pre></li> <li><p>Promises have a <code>then</code> method that allows chaining them. I takes a callback and returns a new Promise which will get resolved with the result of that callback after it was invoked with the first promise's result. If the callback returns a Promise, it will get assimilated instead of getting nested.</p> <pre><code>Promise.prototype.then = function(fn) { var dfd = new Deferred(); // create a new result Deferred this.addCallback(function() { // when `this` resolves… // execute the callback with the results var result = fn.apply(null, arguments); // check whether it returned a promise if (result instanceof Promise) result.addCallback(dfd.resolve); // then hook the resolution on it else dfd.resolve(result); // resolve the new promise immediately }); }); // and return the new Promise return dfd.promise; }; </code></pre></li> </ul> <p>Further concepts would be maintaining a separate <em>error</em> state (with an extra callback for it) and catching exceptions in the handlers, or guaranteeing asynchronity for the callbacks. Once you add those, you've got a fully functional Promise implementation.</p> <p>Here is the error thing written out. It unfortunately is pretty repetitive; you can do better by using extra closures but then it get's really really hard to understand.</p> <pre><code>function Deferred() { var callbacks = [], // list of callbacks errbacks = [], // list of errbacks value, // the fulfill arguments or undefined until they're available reason; // the error arguments or undefined until they're available this.fulfill = function() { if (reason || value) return false; // can't change state value = arguments; // settle the result for (var c;c=callbacks.shift();) c.apply(null, value); errbacks.length = 0; // clear stored errbacks }); this.reject = function() { if (value || reason) return false; // can't change state reason = arguments; // settle the errror for (var c;c=errbacks.shift();) c.apply(null, reason); callbacks.length = 0; // clear stored callbacks }); this.promise = new Promise(function add(c) { if (reason) return; // nothing to do if (value) c.apply(null, value); else callbacks.push(c); }, function add(c) { if (value) return; // nothing to do if (reason) c.apply(null, reason); else errbacks.push(c); }); } function Promise(addC, addE) { this.addCallback = addC; this.addErrback = addE; } Promise.prototype.then = function(fn, err) { var dfd = new Deferred(); this.addCallback(function() { // when `this` is fulfilled… try { var result = fn.apply(null, arguments); if (result instanceof Promise) { result.addCallback(dfd.fulfill); result.addErrback(dfd.reject); } else dfd.fulfill(result); } catch(e) { // when an exception was thrown dfd.reject(e); } }); this.addErrback(err ? function() { // when `this` is rejected… try { var result = err.apply(null, arguments); if (result instanceof Promise) { result.addCallback(dfd.fulfill); result.addErrback(dfd.reject); } else dfd.fulfill(result); } catch(e) { // when an exception was re-thrown dfd.reject(e); } } : dfd.reject); // when no `err` handler is passed then just propagate return dfd.promise; }; </code></pre>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload