I'm having a (seemingly fundamental) problem understanding promises. First the code:
'use strict';
var Q = require("q");
var mockPromise = function (statement) {
var deferred = Q.defer();
console.log("I'm running before I'm queued ...");
setTimeout(function () {
deferred.resolve(statement);
}, 5000);
return deferred.promise;
};
var promises = [
mockPromise("1st statement"),
mockPromise("2nd statement"),
mockPromise("3rd statement")
];
Q.all(promises)
.then(function (results) {
console.log(results);
});
Each promise function gets invoked upon adding it to the promise array, as opposed to when Q.all is called as I thought.
What am I not getting here?
How do I queue an array of promises without immediately invoking said promises?
Promises are objects. They are not 'executed'. They are 'resolved' or 'rejected'. When you create the array, you are executing the mockPromise() function three times. This function is immediately executed in that point of the code.
The mockPromise() function creates a deferred and returns the associated promise. It also sets a timer to resolve the returned promise in the future.
Q.all() just waits for the 3 promises to be 'resolved'. (technically it returns a new promise that will be resolved when the 3 previous promises are resolved)
If you want to execute the three async functions one after the other, I would recommend using the excellent async.js library. It provides many async flow control primitives. In your case you may be interested in series or waterfall methods.
It seems the confusion is that you understand the promise API to be designed for lazy evaluation, which is not the case.
Promises are a way of handling long running requests, they were designed to start IMMEDIATELY to minimize waiting time, and to utilize chaining and joining to clarify how the results of these long running requests should be processed.
You might try to utilize the api Q-Lazy which allows you to delay invocation of promises until they have been subscribed to.
You'd normally defer asynchronous functionality, not just a value. For example:
'use strict';
var Q = require("q");
var mockPromise = function (statement) {
var deferred = Q.defer();
console.log("I'm running before I'm queued ...");
setTimeout(function () {
deferred.resolve(statement());
}, 5000);
return deferred.promise;
};
var promises = [
mockPromise(function() {
console.log("running1");
return "1st statement";
}),
mockPromise(function() {
console.log("running2");
return "2nd statement";
}),
mockPromise(function() {
console.log("running3");
return "3rd statement";
}),
];
Q.all(promises)
.then(function (results) {
console.log(results);
});
Note that the deferred functionality is going to run regardless of whether you ever call a .then on a promise.
Let me show a sample using standard promises. They work pretty much the same as Q promises:
function mockPromise(value) {
return new Promise(resolve => {
console.log("I'm not running before I'm queued ...");
setTimeout(() => {
resolve(value);
}, 1000);
});
}
mockPromise("1st promise").then(x => {
console.log(x);
return mockPromise("2nd promise");
}).then(x => {
console.log(x);
return mockPromise("3nd promise");
}).then(x => {
console.log(x);
});
Related
I am dealing with an old codebase and faced this situation where it's difficult for me to understand the order of execution after promises are resolved. I am more familiar with async/await syntax or with a chain of then-s, but not with this one. Here is the snippet:
_loadCaseDetail: funciton (arg1, arg2, arg3) {
var oDataModel = this.getOwnerComponent().getModel('db2');
loadLatestDatasetVersion(oDataModel).then(function (datasetVersion) {
// do something
});
loadCountries(oDataModel).then(function (countries) {
// do something
});
numberOfRulesetChanges(oDataModel).then(function (elements) {
// do something
});
return fireImplicitLock(caseUuid).then(function (lockData) {
// do something
}).then(function () {
// do something
})
}
loadLatestDatasetVersion, loadCountries, numberOfRulesetChanges, fireImplicitLock - all return promises
My question is: What would be the order in this case for all then-s that come after these promises?
Is it exactly sequential as it is or it's not and we can refactor it with say Promise.all?
Does it even need any refactoring?
What would be the order in this case for all then-s that come after these promises?
The then function will fire when the associated promise resolves. That's the point of asynchronous code. It goes away until it is ready to do the next thing.
we can refactor it with say Promise.all
You could use Promise.all to wait until multiple promises are resolved before doing anything with the resulting values, but that would be inefficient unless the then function for C requires data from A or B.
loadLatestDatasetVersion, loadCountries, numberOfRulesetChanges, fireImplicitLock - all return promises they will get inserted into Event loop one after another.
then part of promises will get executed once promises are resolved. Which promise's then will get executed depends upon the execution of the respective promise.
_loadCaseDetail: funciton (arg1, arg2, arg3) {
var oDataModel = this.getOwnerComponent().getModel('db2');
loadLatestDatasetVersion(oDataModel).then(function (datasetVersion) {
// do something
});
loadCountries(oDataModel).then(function (countries) {
// do something
});
numberOfRulesetChanges(oDataModel).then(function (elements) {
// do something
});
return fireImplicitLock(caseUuid).then(function (lockData) {
// do something
}).then(function () {
// do something
})
}
Promise.all is like a gate where you can pass array of promises and it will resolve only after all the promises are resolved.
let p1 = Promise.resolve(loadLatestDatasetVersion(oDataModel));
let p2 = Promise.resolve(loadCountries(oDataModel));
let p3 = Promise.resolve(numberOfRulesetChanges(oDataModel));
let p4 = Promise.resolve( fireImplicitLock(caseUuid)
let finalPromise = Promise.all([p1,p2,p3,p4]);
finalPromise.then(([datasetVersion,countries,elements,lockData])=>{
// do something here with datasetVersion, countries, elements, lockData you have all the params cause all the promises are resolved.
})
Same thing you can achieve using Promise.all like shown above.
More about promises
return fireImplicitLock(caseUuid).then(function (lockData) {
// do something
}).then(function () {
// do something
})
Above line will not return any result from promises, it will return Promise object with its status resolved/pending/rejected and value.
How to make foreach loop synchronous in AngularJS
var articles = arg;
articles.forEach(function(data){
var promises = [fetchImg(data), fetchUser(data)];
$q.all(promises).then(function (res) {
finalData.push(res[1]);
});
});
return finalData;
I want finalData array to be returned only after the forEach loop gets over. Is there any way to chain it with promises? Something that will execute the foreach loop first and then return the array after the loop is over?
Chaining promises from a foreach loop
Promises are chained by returning values (or a promise) to the handler function in the .then method. Multiple promises are consolidated by using $q.all which itself returns a chainable promise.
function fetchUsers(arg) {
var articles = arg;
var promises = articles.map(function(a){
var subPromises = [fetchImg(a), fetchUser(a)];
return $q.all(subPromises).then(function (res) {
//return for chaining
return {img: res[0], user: res[1]};
});
});
//consolidate promises
var finalPromise = $q.all(promises);
return finalPromise;
};
Because calling the .then method of a promise returns a new derived promise, it is easily possible to create a chain of promises. It is possible to create chains of any length and since a promise can be resolved with another promise (which will defer its resolution further), it is possible to pause/defer resolution of the promises at any point in the chain.1
The returned promise will either resolve fulfilled with an array of users or will resolve rejected with the first error. Final resolution is retrieved with the promise's .then and .catch methods.
fetchUsers(args)
.then ( function onFulfilled(objArray) {
$scope.users = objArray.map(x => x.user);
return objArray;
}).catch ( function onRejected(response) {
console.log("ERROR: ", response);
throw response
})
;
The $q.defer Anti-pattern
The problem with using $q.defer() is that it breaks the promise chain, loses error information, and can create memory leaks when errors are not handled properly. For more information on that see, AngularJS Is this a “Deferred Antipattern”?.
You can modify your code like this:
function fetArticles(arg) {
var articles = arg;
var promises = [], finalData = [];
var deferred = $q.defer();
articles.forEach(function(data) {
var userPromise = fetchUser(data);
userPromise.then(function (res) {
finalData.push(res[1]);
});
promises.push(fetchImg(data));
promises.push(userPrommise);
});
$q.all(promises).then(function() {
deferred.resolve({finalData: finalData, foo: "bar"});
});
return deferred.promise;
}
Now, call this method and register a final callback:
fetArticles(arg).then(function(data) {
console.log("finalData: ", data.finalData, data.foo === "bar");
});
So I have a situation where I have multiple promise chains of an unknown length. I want some action to run when all the CHAINS have been processed. Is that even possible? Here is an example:
app.controller('MainCtrl', function($scope, $q, $timeout) {
var one = $q.defer();
var two = $q.defer();
var three = $q.defer();
var all = $q.all([one.promise, two.promise, three.promise]);
all.then(allSuccess);
function success(data) {
console.log(data);
return data + "Chained";
}
function allSuccess(){
console.log("ALL PROMISES RESOLVED")
}
one.promise.then(success).then(success);
two.promise.then(success);
three.promise.then(success).then(success).then(success);
$timeout(function () {
one.resolve("one done");
}, Math.random() * 1000);
$timeout(function () {
two.resolve("two done");
}, Math.random() * 1000);
$timeout(function () {
three.resolve("three done");
}, Math.random() * 1000);
});
In this example, I set up a $q.all() for promises one, two, and three which will get resolved at some random time. I then add promises onto the ends of one and three. I want the all to resolve when all the chains have been resolved. Here is the output when I run this code:
one done
one doneChained
two done
three done
ALL PROMISES RESOLVED
three doneChained
three doneChainedChained
Is there a way to wait for the chains to resolve?
I want the all to resolve when all the chains have been resolved.
Sure, then just pass the promise of each chain into the all() instead of the initial promises:
$q.all([one.promise, two.promise, three.promise]).then(function() {
console.log("ALL INITIAL PROMISES RESOLVED");
});
var onechain = one.promise.then(success).then(success),
twochain = two.promise.then(success),
threechain = three.promise.then(success).then(success).then(success);
$q.all([onechain, twochain, threechain]).then(function() {
console.log("ALL PROMISES RESOLVED");
});
The accepted answer is correct. I would like to provide an example to elaborate it a bit to those who aren't familiar with promise.
Example:
In my example, I need to replace the src attributes of img tags with different mirror urls if available before rendering the content.
var img_tags = content.querySelectorAll('img');
function checkMirrorAvailability(url) {
// blah blah
return promise;
}
function changeSrc(success, y, response) {
if (success === true) {
img_tags[y].setAttribute('src', response.mirror_url);
}
else {
console.log('No mirrors for: ' + img_tags[y].getAttribute('src'));
}
}
var promise_array = [];
for (var y = 0; y < img_tags.length; y++) {
var img_src = img_tags[y].getAttribute('src');
promise_array.push(
checkMirrorAvailability(img_src)
.then(
// a callback function only accept ONE argument.
// Here, we use `.bind` to pass additional arguments to the
// callback function (changeSrc).
// successCallback
changeSrc.bind(null, true, y),
// errorCallback
changeSrc.bind(null, false, y)
)
);
}
$q.all(promise_array)
.then(
function() {
console.log('all promises have returned with either success or failure!');
render(content);
}
// We don't need an errorCallback function here, because above we handled
// all errors.
);
Explanation:
From AngularJS docs:
The then method:
then(successCallback, errorCallback, notifyCallback) – regardless of when the promise was or will be resolved or rejected, then calls
one of the success or error callbacks asynchronously as soon as the
result is available. The callbacks are called with a single
argument: the result or rejection reason.
$q.all(promises)
Combines multiple promises into a single promise that is resolved when
all of the input promises are resolved.
The promises param can be an array of promises.
About bind(), More info here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
Recently had this problem but with unkown number of promises.Solved using jQuery.map().
function methodThatChainsPromises(args) {
//var args = [
// 'myArg1',
// 'myArg2',
// 'myArg3',
//];
var deferred = $q.defer();
var chain = args.map(methodThatTakeArgAndReturnsPromise);
$q.all(chain)
.then(function () {
$log.debug('All promises have been resolved.');
deferred.resolve();
})
.catch(function () {
$log.debug('One or more promises failed.');
deferred.reject();
});
return deferred.promise;
}
There is a way. $q.all(...
You can check the below stuffs:
http://jsfiddle.net/ThomasBurleson/QqKuk/
http://denisonluz.com/blog/index.php/2013/10/06/angularjs-returning-multiple-promises-at-once-with-q-all/
You can use "await" in an "async function".
app.controller('MainCtrl', async function($scope, $q, $timeout) {
...
var all = await $q.all([one.promise, two.promise, three.promise]);
...
}
NOTE: I'm not 100% sure you can call an async function from a non-async function and have the right results.
That said this wouldn't ever be used on a website. But for load-testing/integration test...maybe.
Example code:
async function waitForIt(printMe) {
console.log(printMe);
console.log("..."+await req());
console.log("Legendary!")
}
function req() {
var promise = new Promise(resolve => {
setTimeout(() => {
resolve("DARY!");
}, 2000);
});
return promise;
}
waitForIt("Legen-Wait For It");
The question applies to promises in general and isn't specific to Angular, but the example makes use of Angular $q and service singletons.
Here is a plunker
var app = angular.module('app', []);
app.factory('onetimeResolvingService', function ($q) {
var promise = $q(function(resolve, reject) {
setTimeout(function () {
resolve();
}, 500);
});
return promise;
});
app.controller('AController', function (onetimeResolvingService, $q) {
onetimeResolvingService.then(function () {
console.log('A resolved');
return $q.reject();
}).then(function () {
console.log('A resolved');
});
});
app.controller('BController', function (onetimeResolvingService, $q) {
onetimeResolvingService.then(function () {
console.log('B resolved');
return $q.reject();
}).then(function () {
console.log('B resolved');
});
});
and the document is
<body ng-app="app">
<div ng-controller="AController"></div>
<div ng-controller="BController"></div>
</body>
It will naturally output
A resolved
B resolved
What would be a good pattern to make the singleton promise resolve only the first time, i.e.
A resolved
and not the subsequent times?
Something like onetimeResolvingService.$$state.status = 2 could possibly can do the trick, but it looks like $q hack and smells bad.
What would be a good pattern to make the singleton promise resolve only the first time
To not to. One of the key facets of a promise is that once it's settled, it's settled, and both the settled state (resolved or rejected) and the value are at that point unchanging. See §2.1.2 and §2.1.3 of the A+ promises spec:
2.1.2 When fulfilled, a promise:
2.1.2.1 must not transition to any other state.
2.1.2.2 must have a value, which must not change.
2.1.3 When rejected, a promise:
2.1.3.1 must not transition to any other state.
2.1.3.2 must have a reason, which must not change.
If the callbacks added via then are not satisfied at some stage (e.g., your second hookup), it's not a promise. It's something...else.
T.J. Crowder is correct in that the functionality you're looking for in a promise does not exist. The question of how to achieve what you're looking for however can be found in a structure like below:
function OnetimeValue($q) {
var promisedValue = $q(function(resolve, reject) {
setTimeout(function () {resolve('The one time value');}, 500);
});
var valueGot = false;
this.GetValue = function GetValue() {
var res;
if (!valueGot) {
res = promisedValue;
} else {
res = $q(function(resolve, reject) {
resolve(null);
});
}
valueGot = true;
return res;
};
}
Assuming you new this once (as angular services do), GetValue() will return the promisified string upon the first call. Subsequent calls return null.
This plunker shows the above in action
Edit: Whoops, misread the question.
There is now a way to do this with EcmaScript promises. The static Promise.resolve() method takes a promise and waits on its value; if it has already been resolved, it simply returns the value.
For example, here's how we're using this method to make multiple calls to fetchQuery rely on a single async authentication call:
fetchQuery gets an auth token (JWT) with:
const authToken = await AuthToken.get();
And AuthToken looks like (TypeScript):
class AuthToken {
private static _accessTokenPromise: Promise<string>;
public static async get() {
if (!this._accessTokenPromise)
this._accessTokenPromise = this.AuthFunction(); // AuthFunction returns a promise
return await Promise.resolve(this._accessTokenPromise);
}
}
Or simply call then() twice on the same promise object per the OP's question to have two calls wait on the same async operation.
If you're using lodash you can simply use memoize like this:
const returnFirstRunResultAlways = _.memoize(async function(params) { ... })
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 7 years ago.
Improve this question
How is a promise/defer library like q implemented? I was trying to read the source code but found it pretty hard to understand, so I thought it'd be great if someone could explain to me, from a high level, what are the techniques used to implement promises in single-thread JS environments like Node and browsers.
I find it harder to explain than to show an example, so here is a very simple implementation of what a defer/promise could be.
Disclaimer: This is not a functional implementation and some parts of the Promise/A specification are missing, This is just to explain the basis of the promises.
tl;dr: Go to the Create classes and example section to see full implementation.
Promise:
First we need to create a promise object with an array of callbacks. I'll start working with objects because it's clearer:
var promise = {
callbacks: []
}
now add callbacks with the method then:
var promise = {
callbacks: [],
then: function (callback) {
callbacks.push(callback);
}
}
And we need the error callbacks too:
var promise = {
okCallbacks: [],
koCallbacks: [],
then: function (okCallback, koCallback) {
okCallbacks.push(okCallback);
if (koCallback) {
koCallbacks.push(koCallback);
}
}
}
Defer:
Now create the defer object that will have a promise:
var defer = {
promise: promise
};
The defer needs to be resolved:
var defer = {
promise: promise,
resolve: function (data) {
this.promise.okCallbacks.forEach(function(callback) {
window.setTimeout(function () {
callback(data)
}, 0);
});
},
};
And needs to reject:
var defer = {
promise: promise,
resolve: function (data) {
this.promise.okCallbacks.forEach(function(callback) {
window.setTimeout(function () {
callback(data)
}, 0);
});
},
reject: function (error) {
this.promise.koCallbacks.forEach(function(callback) {
window.setTimeout(function () {
callback(error)
}, 0);
});
}
};
Note that the callbacks are called in a timeout to allow the code be always asynchronous.
And that's what a basic defer/promise implementation needs.
Create classes and example:
Now lets convert both objects to classes, first the promise:
var Promise = function () {
this.okCallbacks = [];
this.koCallbacks = [];
};
Promise.prototype = {
okCallbacks: null,
koCallbacks: null,
then: function (okCallback, koCallback) {
okCallbacks.push(okCallback);
if (koCallback) {
koCallbacks.push(koCallback);
}
}
};
And now the defer:
var Defer = function () {
this.promise = new Promise();
};
Defer.prototype = {
promise: null,
resolve: function (data) {
this.promise.okCallbacks.forEach(function(callback) {
window.setTimeout(function () {
callback(data)
}, 0);
});
},
reject: function (error) {
this.promise.koCallbacks.forEach(function(callback) {
window.setTimeout(function () {
callback(error)
}, 0);
});
}
};
And here is an example of use:
function test() {
var defer = new Defer();
// an example of an async call
serverCall(function (request) {
if (request.status === 200) {
defer.resolve(request.responseText);
} else {
defer.reject(new Error("Status code was " + request.status));
}
});
return defer.promise;
}
test().then(function (text) {
alert(text);
}, function (error) {
alert(error.message);
});
As you can see the basic parts are simple and small. It will grow when you add other options, for example multiple promise resolution:
Defer.all(promiseA, promiseB, promiseC).then()
or promise chaining:
getUserById(id).then(getFilesByUser).then(deleteFile).then(promptResult);
To read more about the specifications: CommonJS Promise Specification. Note that main libraries (Q, when.js, rsvp.js, node-promise, ...) follow Promises/A specification.
Hope I was clear enough.
Edit:
As asked in the comments, I've added two things in this version:
The possibility to call then of a promise, no matter what status it has.
The possibility to chain promises.
To be able to call the promise when resolved you need to add the status to the promise, and when the then is called check that status. If the status is resolved or rejected just execute the callback with its data or error.
To be able to chain promises you need to generate a new defer for each call to then and, when the promise is resolved/rejected, resolve/reject the new promise with the result of the callback. So when the promise is done, if the callback returns a new promise it is bound to the promise returned with the then(). If not, the promise is resolved with the result of the callback.
Here is the promise:
var Promise = function () {
this.okCallbacks = [];
this.koCallbacks = [];
};
Promise.prototype = {
okCallbacks: null,
koCallbacks: null,
status: 'pending',
error: null,
then: function (okCallback, koCallback) {
var defer = new Defer();
// Add callbacks to the arrays with the defer binded to these callbacks
this.okCallbacks.push({
func: okCallback,
defer: defer
});
if (koCallback) {
this.koCallbacks.push({
func: koCallback,
defer: defer
});
}
// Check if the promise is not pending. If not call the callback
if (this.status === 'resolved') {
this.executeCallback({
func: okCallback,
defer: defer
}, this.data)
} else if(this.status === 'rejected') {
this.executeCallback({
func: koCallback,
defer: defer
}, this.error)
}
return defer.promise;
},
executeCallback: function (callbackData, result) {
window.setTimeout(function () {
var res = callbackData.func(result);
if (res instanceof Promise) {
callbackData.defer.bind(res);
} else {
callbackData.defer.resolve(res);
}
}, 0);
}
};
And the defer:
var Defer = function () {
this.promise = new Promise();
};
Defer.prototype = {
promise: null,
resolve: function (data) {
var promise = this.promise;
promise.data = data;
promise.status = 'resolved';
promise.okCallbacks.forEach(function(callbackData) {
promise.executeCallback(callbackData, data);
});
},
reject: function (error) {
var promise = this.promise;
promise.error = error;
promise.status = 'rejected';
promise.koCallbacks.forEach(function(callbackData) {
promise.executeCallback(callbackData, error);
});
},
// Make this promise behave like another promise:
// When the other promise is resolved/rejected this is also resolved/rejected
// with the same data
bind: function (promise) {
var that = this;
promise.then(function (res) {
that.resolve(res);
}, function (err) {
that.reject(err);
})
}
};
As you can see, it has grown quite a bit.
Q is a very complex promise library in terms of implementation because it aims to support pipelining and RPC type scenarios. I have my own very bare bones implementation of the Promises/A+ specification here.
In principle it's quite simple. Before the promise is settled/resolved, you keep a record of any callbacks or errbacks by pushing them into an array. When the promise is settled you call the appropriate callbacks or errbacks and record what result the promise was settled with (and whether it was fulfilled or rejected). After it's settled, you just call the callbacks or errbacks with the stored result.
That gives you aproximately the semantics of done. To build then you just have to return a new promise that is resolved with the result of calling the callbacks/errbacks.
If you're interested in a full explenation of the reasonning behind the development of a full on promise implementation with support for RPC and pipelining like Q, you can read kriskowal's reasonning here. It's a really nice graduated approach that I can't recommend highly enough if you are thinking of implementing promises. It's probably worth a read even if you're just going to be using a promise library.
As Forbes mentions in his answer, I chronicled many of the design decisions involved in making a library like Q, here https://github.com/kriskowal/q/tree/v1/design. Suffice it to say, there are levels of a promise library, and lots of libraries that stop at various levels.
At the first level, captured by the Promises/A+ specification, a promise is a proxy for an eventual result and is suitable for managing “local asynchrony”. That is, it is suitable for ensuring that work occurs in the right order, and for ensuring that it is simple and straight-forward to listen for the result of an operation regardless of whether it already settled, or will occur in the future. It also makes it just as simple for one or many parties to subscribe to an eventual result.
Q, as I have implemented it, provides promises that are proxies for eventual, remote, or eventual+remote results. To that end, it’s design is inverted, with different implementations for promises—deferred promises, fulfilled promises, rejected promises, and promises for remote objects (the last being implemented in Q-Connection). They all share the same interface and work by sending and receiving messages like "then" (which is sufficient for Promises/A+) but also "get" and "invoke". So, Q is about “distributed asynchrony”, and exists on another layer.
However, Q was actually taken down from a higher layer, where promises are used for managing distributed asynchrony among mutually suspicious parties like you, a merchant, a bank, Facebook, the government—not enemies, maybe even friends, but sometimes with conflicts of interest. The Q that I implemented is designed to be API compatible with hardened security promises (which is the reason for separating promise and resolve), with the hope that it would introduce people to promises, train them in using this API, and allow them to take their code with them if they need to use promises in secure mashups in the future.
Of course, there are trade-offs as you move up the layers, usually in speed. So, promises implementations can also be designed to co-exist. This is where the concept of a “thenable” enters. Promise libraries at each layer can be designed to consume promises from any other layer, so multiple implementations can coexist, and users can buy only what they need.
All this said, there is no excuse for being difficult to read. Domenic and I are working on a version of Q that will be more modular and approachable, with some of its distracting dependencies and work-arounds moved into other modules and packages. Thankfully folks like Forbes, Crockford, and others have filled in the educational gap by making simpler libraries.
First make sure you're understanding how Promises are supposed to work. Have a look at the CommonJs Promises proposals and the Promises/A+ specification for that.
There are two basic concepts that can be implemented each in a few simple lines:
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.
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;
}
Promises have a then 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.
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;
};
Further concepts would be maintaining a separate error 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.
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.
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;
};
You might want to check out the blog post on Adehun.
Adehun is an extremely lightweight implementation (about 166 LOC) and very useful for learning how to implement the Promise/A+ spec.
Disclaimer: I wrote the blog post but the blog post does explain all about Adehun.
The Transition function – Gatekeeper for State Transition
Gatekeeper function; ensures that state transitions occur when all required conditions are met.
If conditions are met, this function updates the promise’s state and value. It then triggers the process function for further processing.
The process function carries out the right action based on the transition (e.g. pending to fulfilled) and is explained later.
function transition (state, value) {
if (this.state === state ||
this.state !== validStates.PENDING ||
!isValidState(state)) {
return;
}
this.value = value;
this.state = state;
this.process();
}
The Then function
The then function takes in two optional arguments (onFulfill and onReject handlers) and must return a new promise. Two major requirements:
The base promise (the one on which then is called) needs to create a new promise using the passed in handlers; the base also stores an internal reference to this created promise so it can be invoked once the base promise is fulfilled/rejected.
If the base promise is settled (i.e. fulfilled or rejected), then the appropriate handler should be called immediately. Adehun.js handles this scenario by calling process in the then function.
``
function then(onFulfilled, onRejected) {
var queuedPromise = new Adehun();
if (Utils.isFunction(onFulfilled)) {
queuedPromise.handlers.fulfill = onFulfilled;
}
if (Utils.isFunction(onRejected)) {
queuedPromise.handlers.reject = onRejected;
}
this.queue.push(queuedPromise);
this.process();
return queuedPromise;
}`
The Process function – Processing Transitions
This is called after state transitions or when the then function is invoked. Thus it needs to check for pending promises since it might have been invoked from the then function.
Process runs the Promise Resolution procedure on all internally stored promises (i.e. those that were attached to the base promise through the then function) and enforces the following Promise/A+ requirements:
Invoking the handlers asynchronously using the Utils.runAsync helper (a thin wrapper around setTimeout (setImmediate will also work)).
Creating fallback handlers for the onSuccess and onReject handlers if they are missing.
Selecting the correct handler function based on the promise state e.g. fulfilled or rejected.
Applying the handler to the base promise’s value. The value of this operation is passed to the Resolve function to complete the promise processing cycle.
If an error occurs, then the attached promise is immediately rejected.
function process() {
var that = this,
fulfillFallBack = function(value) {
return value;
},
rejectFallBack = function(reason) {
throw reason;
};
if (this.state === validStates.PENDING) {
return;
}
Utils.runAsync(function() {
while (that.queue.length) {
var queuedP = that.queue.shift(),
handler = null,
value;
if (that.state === validStates.FULFILLED) {
handler = queuedP.handlers.fulfill ||
fulfillFallBack;
}
if (that.state === validStates.REJECTED) {
handler = queuedP.handlers.reject ||
rejectFallBack;
}
try {
value = handler(that.value);
} catch (e) {
queuedP.reject(e);
continue;
}
Resolve(queuedP, value);
}
});
}
The Resolve function – Resolving Promises
This is probably the most important part of the promise implementation since it handles promise resolution. It accepts two parameters – the promise and its resolution value.
While there are lots of checks for various possible resolution values; the interesting resolution scenarios are two – those involving a promise being passed in and a thenable (an object with a then value).
Passing in a Promise value
If the resolution value is another promise, then the promise must adopt this resolution value’s state. Since this resolution value can be pending or settled, the easiest way to do this is to attach a new then handler to the resolution value and handle the original promise therein. Whenever it settles, then the original promise will be resolved or rejected.
Passing in a thenable value
The catch here is that the thenable value’s then function must be invoked only once (a good use for the once wrapper from functional programming). Likewise, if the retrieval of the then function throws an Exception, the promise is to be rejected immediately.
Like before, the then function is invoked with functions that ultimately resolve or reject the promise but the difference here is the called flag which is set on the first call and turns subsequent calls are no ops.
function Resolve(promise, x) {
if (promise === x) {
var msg = "Promise can't be value";
promise.reject(new TypeError(msg));
}
else if (Utils.isPromise(x)) {
if (x.state === validStates.PENDING){
x.then(function (val) {
Resolve(promise, val);
}, function (reason) {
promise.reject(reason);
});
} else {
promise.transition(x.state, x.value);
}
}
else if (Utils.isObject(x) ||
Utils.isFunction(x)) {
var called = false,
thenHandler;
try {
thenHandler = x.then;
if (Utils.isFunction(thenHandler)){
thenHandler.call(x,
function (y) {
if (!called) {
Resolve(promise, y);
called = true;
}
}, function (r) {
if (!called) {
promise.reject(r);
called = true;
}
});
} else {
promise.fulfill(x);
called = true;
}
} catch (e) {
if (!called) {
promise.reject(e);
called = true;
}
}
}
else {
promise.fulfill(x);
}
}
The Promise Constructor
And this is the one that puts it all together. The fulfill and reject functions are syntactic sugar that pass no-op functions to resolve and reject.
var Adehun = function (fn) {
var that = this;
this.value = null;
this.state = validStates.PENDING;
this.queue = [];
this.handlers = {
fulfill : null,
reject : null
};
if (fn) {
fn(function (value) {
Resolve(that, value);
}, function (reason) {
that.reject(reason);
});
}
};
I hope this helped shed more light into the way promises work.