Is it possible to have multiple thenable for same promise instance? - javascript

Will it work when i have multiple thenable functions for single promise object? I don't want nesting in this case. I want to perform different operation on the same promise object.
var a = 0,
t = null,
promiseFun = function () {
return new Ext.Promise(function (resolve, reject) {
t = setInterval(function () {
if (a == 1) {
resolve();
clearInterval(t);
} else if (a == 2) {
reject();
clearInterval(t);
}
}, 1000);
})
};
var promiseObj = promiseFun();
//handler one
promiseObj.then(function () {
console.log('resolved 1')
}, function () {
console.log('rejected 1')
}).then(function () {
console.log('resolved Nest')
}, function () {
console.log('rejected Nest')
});
//handler two- this is valid ?
promiseObj.then(function () {
console.log('resolved 2')
}, function () {
console.log('rejected 2')
});
//OUTPUT:
//resolved 1
//resolved 2
//resolved Nest
Or do i need to wrap in Deffered.

Will it work when i have multiple thenable functions for single promise object?
Yes, that works just fine. This is referred to as branching instead of chaining.
If you have this:
let p = somePromiseGeneratingFunction();
p.then(...).then(...);
p.then(...).then(...);
Then, you've just create two separate and independent promise chains that are each started when p resolves. If that's the logic you want, then that's perfectly OK.
The one thing you can guarantee is that the first p.then() handler will get called before the second p.then() handler gets called, but if there are further asynchronous operations in either chain, then they run independently after that and are no longer coordinated with each other. They are separate and independent promise chains that run according to the timing of their own asynchronous operations. That's why it's referred to as "branching". One chain branches into two chains.
See Is there a difference between promise.then.then vs promise.then; promise.then for a bit more discussion on the topic.

Related

How to create a sequential promise loop with variable function calls

I need to somehow loop over the work array passed to _start then
for each of the items in the array, I need to somehow call the corresponding function with the same name.
I don't have control over the number of items in work the array or the number of items, I do know that there will always be a corresponding function.
I don't want to call all the functions at the same time, once the first function resolves after 3 seconds, I then want to call the second function, once the second function resolves after 3 seconds I then want to call the third function. Once the third function resolves after another 3 seconds I want to call _done().
In this example each function takes 3 seconds to complete _done wont gete called for 9 seconds.
function _start(data){
// Insert some kinda native magic loop
}
function _one(){
return new Promise((resolve, reject) => {
setTimeout(function(){
resolve(1);
}, 3000);
})
};
function _two(){
return new Promise((resolve, reject) => {
setTimeout(function(){
resolve(2);
}, 3000);
})
};
function _done(){
console.log('All done in 9 seconds)
}
(function(){
var work = ['_one', '_two', '_two'];
_start(work);
})();
Given the order is dicated by the array, you can use reduce to aggregate the promises into a chain
const _start = (...actions) => {
return actions.reduce((chain, action) => {
const func = this[action];
return chain.then(() => func());
}, Promise.resolve());
}
...
_start('_one', '_two', '_three').then(() => console.log('All done'));
See it in action - the example appends an extra then to the chain just to output any results from the promises (probably outwith the scope of this question but something you may have to consider if getting data back is required).
Update
Can see you intend on invoking _start from a different context in which the functions are declared, this is fine but you need to make sure you set the correct context before hand i.e.
const self = this;
(function() {
_start.bind(self)('_one', '_two', '_two');
})();
A function which creates a promise which sleeps:
const sleep = n => () => new Promise(resolve => setTimeout(resolve, n));
A function which sleeps after some input promise:
const sleepAfter = n => p => p.then(sleep(n));
A function which chains a bunch of promises, represented by functions:
const chain = (...promises) => promises.reduce((ret, promise) => ret.then(promise),
Promise.resolve());
Run a bunch of functions yielding promises, sleeping in between:
const _start = promises => chain(promises.map(sleepAfter(3000)));
Now just:
_start(_one, _two, _three).then(_done);
Try using this:
_one().then((firstResponse) {
return_two();
}) .then((secondResponse) => {
*second and first respone are already done*
});
Use promises then
_one().then((responseOne) => {
return _two();
}).then((responseTwo) => {
// _one & _two are done
});

Linking promise chains

I have a working promise chain:
function startSync(db) {
var promise = new Promise(function(resolve, reject) {
syncCats(db)
.then(syncTrees(db))
.then(syncCars(db))
...
.then(resolve());
});
return promise;
}
This works great. It performs each of those function calls, waiting for each one to complete before firing off another. Each of those functions returns a promise, like so:
function syncCaesar(db) {
var promise = new Promise(resolve, reject) {
// do aysnc db calls, and whatnot, resolving/rejecting appropriately
}
return promise;
}
I need to run this chain on a series of databases, sequentially.
I've tried other solutions here, but they would fire off syncCats() all at once.
For instance:
var promise = Promise.resolve();
dbs.forEach(function(db) {
promise = promise.then(startSync(db));
}
Fires off syncCats(db[0]) and syncCats(db[1]) simultaneously.
Edit:
Performing startSync(dbs[0]).then(startSync(dbs[1])); acts the same.
Edit2: Also performs the same:
dbs.forEach(function(db) {
promise = promise.then(function() {
return startSync(db);
}
}
You're calling startSync and then passing its return value in to then. So naturally, if you do that twice, it's going to start the process twice in parallel.
Instead, pass in a function that doesn't call startSync until it's called:
var promise = Promise.resolve();
dbs.forEach(function(db) {
promise = promise.then(function() { startSync(db); });
});
or with ES2015:
let promise = Promise.resolve();
dbs.forEach(function(db) {
promise = promise.then(_ => startSync(db));
});
Separately, three things jump out about startSync:
It starts all its operations in parallel, not sequentially
It exhibits the promise creation anti-pattern. There's no reason for startSync to create a new promise; it already has a promise it can work with
It ensures that its resolution value is undefined
If you really want the operations running in parallel like that, I suggest being more explicit about it:
function startSync(db) {
return Promise.all([
syncCats(db),
syncTrees(db),
syncCars(db)
])
.then(_ => undefined); // This does #3
}
...but you could also do to avoid the anti-pattern:
// Still run in parallel!
function startSync(db) {
return syncCats(db)
.then(syncTrees(db))
.then(syncCars(db))
.then(_ => undefined); // Does #3
}
If you meant for them to be sequential, you need to pass functions, not the result of calling them:
function startSync(db) {
return syncCats(db)
.then(_ => syncTrees(db))
.then(_ => syncCars(db))
.then(_ => undefined); // Again, does #3
}
If you made syncCats, syncTrees, and syncCars resolve their promises with db, like this:
function syncCats(db) {
return startSomethingAsync().then(_ => db);
}
...then it could be:
function startSync(db) {
return syncCats(db)
.then(syncTrees)
.then(syncCars)
.then(_ => undefined); // Only here for #3
}
...since each would receive db as its first argument.
Finally, if you don't need to force undefined as the promise resolution value, I suggest dropping that part from the above. :-)
With appreciation to T.J and Jaromanda, I was able to solve it by fixing how startSync() was resolved:
function startSync(db) {
var promise = new Promise(function(resolve, reject) {
syncCats(db)
.then(syncTrees(db)) // not correct way to call, see below
.then(syncCars(db))
...
.then(function() { resolve(); });
});
return promise;
}
Now, as T.J points out, the way each of the .then's is being called is incorrect. He explains it better in his answer. I'm being saved by some misunderstood database layer queueing. syncTrees() and syncCars() should be running in parallel.
If I understood it correctly, you will be having an array of databases and you want to sync them one by one i.e. sync for dbs[0], once that is complete sync for dbs[1], and so on. If that's correct you can do something like this.
var syncMultipleDBs = (dataBases) {
var db = dataBases.shift();
if (!db) {
return;
}
startSync(db).then(syncMultipleDBs.bind(null, dataBases));
};
syncsyncMultipleDBs(dbs.slice());
I hope this will help.

Are JavaScript Promise asynchronous?

Just a quick question of clarifications: is JavaScript Promise asynchronous? I've been reading a lot of posts on Promise and async programming (namely ajax requests). If Promise is not async, how do we make it so?
For example, I have a function to wrap a function f with argument array args inside a Promise. Nothing about f inherently is async.
function getPromise(f, args) {
return new Promise(function(resolve, reject) {
var result = f.apply(undefined, args);
resolve(result);
});
}
To make this async, I read some SO posts and decided that the setTimeout is what a lot of people were recommending to make code non-blocking.
function getPromise(f, args) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
var r = f.apply(undefined, args);
resolve(r);
}, 0);
});
}
Would this approach with setTimeout work to make code non-blocking inside a Promise?
(Note that I am not relying on any third-party Promise API, just what is supported by the browsers).
I think you are working under a misunderstanding. JavaScript code is always* blocking; that is because it runs on a single thread. The advantages of the asynchronous style of coding in Javascript is that external operations like I/O do not require blocking that thread. The callback that processes the response from the I/O is still blocking though and no other JavaScript can run concurrently.
* Unless you consider running multiple processes (or WebWorkers in a browser context).
Now for your specific questions:
Just a quick question of clarifications: is JavaScript Promise asynchronous?
No, the callback passed into the Promise constructor is executed immediately and synchronously, though it is definitely possible to start an asynchronous task, such as a timeout or writing to a file and wait until that asynchronous task has completed before resolving the promise; in fact that is the primary use-case of promises.
Would this approach with setTimeout work to make code non-blocking inside a Promise?
No, all it does is change the order of execution. The rest of your script will execute until completion and then when there is nothing more for it to do the callback for setTimeout will be executed.
For clarification:
console.log( 'a' );
new Promise( function ( ) {
console.log( 'b' );
setTimeout( function ( ) {
console.log( 'D' );
}, 0 );
} );
// Other synchronous stuff, that possibly takes a very long time to process
console.log( 'c' );
The above program deterministically prints:
a
b
c
D
That is because the callback for the setTimeout won't execute until the main thread has nothing left to do (after logging 'c').
const p = new Promise((resolve, reject) => {
if (1 + 1 === 2) {
resolve("A");
} else {
reject("B");
}
});
p.then((name) => console.log(name)).catch((name) => console.log(name));
console.log("hello world");
Promise doesn't block the next lines while it's in pending state. So, it works asynchronously.
Your MDN reference was helpful. Thx.
If you run this, you should see
asynchronous output.
================================================================
asynchronous using "Promise"
const log = console.log;
//---------------------------------------
class PromiseLab {
play_promise_chain(start) {
//"then" returns a promise, so we can chain
const promise = new Promise((resolve, reject) => {
resolve(start);
});
promise.then((start) => {
log(`Value: "${start}" -- adding one`);
return start + 1;
}).then((start) => {
log(`Value: "${start}" -- adding two`);
return start + 2;
}).then((start) => {
log(`Value: "${start}" -- adding three`);
return start + 3;
}).catch((error) => {
if (error) log(error);
});
}
}
//---------------------------------------
const lab = new PromiseLab();
lab.play_promise_chain(100);
lab.play_promise_chain(200);
Output should be asynchronous something like:
Value: "100" -- adding one
Value: "200" -- adding one
Value: "101" -- adding two
Value: "201" -- adding two
Value: "103" -- adding three
Value: "203" -- adding three
================================================================
Synchronous using "MyPromise" (e.g. basic js code)
const log = console.log;
//---------------------------------------
class MyPromise {
value(value) { this.value = value; }
error(err) { this.error = err; }
constructor(twoArgFct) {
twoArgFct(
aValue => this.value(aValue),
anError => this.error(anError));
}
then(resultHandler) {
const result = resultHandler(this.value);
return new MyPromise((resolve, reject) => {
resolve(result);
});
}
catch(errorHandler) {
errorHandler(this.error());
}
}
//--------------------------------------
class MyPromiseLab {
play_promise_chain(start) {
//"then" returns a promise, so we can chain
const promise = new MyPromise((resolve, reject) => {
resolve(start);
});
promise.then((start) => {
log(`Value: "${start}" -- adding one`);
return start + 1;
}).then((start) => {
log(`Value: "${start}" -- adding two`);
return start + 2;
}).then((start) => {
log(`Value: "${start}" -- adding three`);
return start + 3;
}).catch((error) => {
if (error) log(error);
});
}
}
//---------------------------------------
const lab = new MyPromiseLab();
lab.play_promise_chain(100);
lab.play_promise_chain(200);
Output should be synchronous:
Value: "100" -- adding one
Value: "101" -- adding two
Value: "103" -- adding three
Value: "200" -- adding one
Value: "201" -- adding two
Value: "203" -- adding three

How do promises work in JavaScript?

I just implemented my first function that returns a promise based on another promise in AngularJS, and it worked. But before I decided to just do it, I spent 2 hours reading and trying to understand the concepts behind promises. I thought if I could write a simple piece of code that simulated how promises worked, I would then be able to conceptually understand it instead of being able to use it without really knowing how it works. I couldn't write that code.
So, could someone please illustrate in vanilla JavaScript how promises work?
A promise is basically an object with two methods. One method is for defining what to do, and one is for telling when to do it. It has to be possible to call the two methods in any order, so the object needs to keep track of which one has been called:
var promise = {
isDone: false,
doneHandler: null,
done: function(f) {
if (this.isDone) {
f();
} else {
this.doneHandler = f;
}
},
callDone: function() {
if (this.doneHandler != null) {
this.doneHandler();
} else {
this.isDone = true;
}
}
};
You can define the action first, then trigger it:
promise.done(function(){ alert('done'); });
promise.callDone();
You can trigger the action first, then define it:
promise.callDone();
promise.done(function(){ alert('done'); });
Demo: http://jsfiddle.net/EvN9P/
When you use a promise in an asynchronous function, the function creates the empty promise, keeps a reference to it, and also returns the reference. The code that handles the asynchronous response will trigger the action in the promise, and the code calling the asynchronous function will define the action.
As either of those can happen in any order, the code calling the asynchronous function can hang on to the promise and define the action any time it wants.
For the simplicity to understand about the promises in Javascript.
You can refer below example. Just copy paste in a new php/html file and run.
<!DOCTYPE HTML>
<html>
<head>
<script type="text/javascript">
function test(n){
alert('input:'+n);
var promise = new Promise(function(fulfill, reject) {
/*put your condition here */
if(n) {
fulfill("Inside If! match found");
}
else {
reject(Error("It broke"));
}
});
promise.then(function(result) {
alert(result); // "Inside If! match found"
}, function(err) {
alert(err); // Error: "It broke"
});
}
</script>
</head>
<body>
<input type="button" onclick="test(1);" value="Test"/>
</body>
</html>
Click on Test button,
It will create new promise,
if condition will be true it fulfill the response,
after that promise.then called and based on the fulfill it will print the result.
In case of reject promise.then returns the error message.
Probably the simplest example of promises usage looks like that:
var method1 = (addings = '') => {
return new Promise(resolve => {
console.log('method1' + addings)
resolve(addings + '_adding1');
});
}
var method2 = (addings = '') => {
return new Promise(resolve => {
console.log('method2' + addings)
resolve(addings + '_adding2');
});
}
method1().then(method2).then(method1).then(method2);
// result:
// method1
// method2_adding1
// method1_adding1_adding2
// method2_adding1_adding2_adding1
That's basic of basics. Having it, you can experiment with rejects:
var method1 = (addings = '*') => {
return new Promise((resolve, reject) => {
console.log('method1' + addings)
resolve(addings + '_adding1');
});
}
var method2 = (addings = '*') => {
return new Promise((resolve, reject) => {
console.log('method2' + addings)
reject();
});
}
var errorMethod = () => {
console.log('errorMethod')
}
method1()
.then(method2, errorMethod)
.then(method1, errorMethod)
.then(method2, errorMethod)
.then(method1, errorMethod)
.then(method2, errorMethod);
// result:
// method1*
// method2*_adding1
// errorMethod
// method2*
// errorMethod
// method2*
As we can see, in case of failure error function is fired (which is always the second argument of then) and then next function in chain is fired with no given argument.
For advanced knowledge I invite you here.
please check this simple promise code. this will help you to better understand of promise functionality.
A promise is an object that may produce a single value some time in the future: either a resolved value, or a reason that it’s not resolved. A promise may be in one of 3 possible states: fulfilled, rejected, or pending. Promise users can attach callbacks to handle the fulfilled value or the reason for rejection.
let myPromise = new Promise((resolve, reject)=>{
if(2==2){
resolve("resolved")
}else{
reject("rejected")
}
});
myPromise.then((message)=>{
document.write(`the promise is ${message}`)
}).catch((message)=>{
document.write(`the promise is ${message}`)
})
check this out

How is a promise/defer library implemented? [closed]

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.

Categories

Resources