This is a fork of Kristopher Kowal's Q with one difference -- this version of the library allows data to be shared easily between promises, as long as they are part of the same promise chain or any of its subchains.
Function scope is a great approach when your code allows it. For example:
function run()
{
var myVariable;
Q(true)
.then(function() {
return Q(true)
.then(function() {
myVariable = 12;
});
})
.then(function() {
return Q(true)
.then(function() {
console.log(myVariable) // outputs "12"
});
});
}
That works brilliantly. However, function scope is not always available. Consider the following:
var ClassA = function() {};
ClassA.prototype.setVariable = function() {
return function() {
return Q(true)
.then(function() {
myVariable = 12; // Undefined variable error
});
};
};
module.exports = ClassA;
var ClassB = function() {};
ClassB.prototype.logVariable = function() {
return function() {
return Q(true)
.then(function() {
console.log(myVariable); // Undefined variable error
});
};
};
module.exports = ClassB;
var ClassA = require('./class-a'),
ClassB = require('./class-b');
function run2()
{
var myVariable,
a = new ClassA(),
b = new ClassB();
Q(true).then(a.setVariable()).then(b.logVariable());
}
Since setVariable()
and logVariable()
do not share context with run()
,
myVariable
will remain undefined within ClassA
and ClassB
.
Standard approach to fix the code above would be to pass myVariable
to
the objects eiter as an argument in the function calls or in their constructors.
This works well, provided that you have access to the variable(s) at all times when
you need to use them. However, it also forces you to pass the variables to
different parts of your chain, and may even force you to pass the variables through
chains that have nothing to do with your variables just so that you can get to
the variables at the parts where they are needed.
When dealing with long promise chains and, particularly, subchains generated by code in multiple modules, passing variables down the promise chains gets very cumbersome and increases the likelihood of human error, as accidentally omitting one variable from being passed will fail all downstream parts of the chain relying on that variable.
If you are dealing with relatively simple promise chains, the two strategies described above should satisfy your needs, despite their limitations.
The local data context is a solution for complex promise chains, in which isolated promises in the same chain (or in separate subchains which are connected through a superchain) need to share data between each other.
Instead of forcing developers to pass arguments through the chain manually,
Q-Local introduces an new function promise.local(function(localData) {})
, which shares
the localData
object across all members of the promise chain and subchains.
Note that unconnected chains will not share the same localData
object -- each
unconnected chain will have its own localData
object.
Q(true)
.local(function(localData) {
localData.myVariable = 1001;
});
Q(true)
.local(function(localData) {
// (Will print out 'undefined', unless connected to the previous chain, of course)
console.log(localData.myVariable);
});
Function context:
function run()
{
var myVariable;
Q(true)
.then(function() {
return Q(true)
.local(function(localData) {
localData.myVariable = 12;
});
})
.then(function() {
return Q(true)
.local(function(localData) {
console.log(localData.myVariable); // outputs "12"
});
});
}
Modules:
var ClassA = function() {};
ClassA.prototype.setVariable = function() {
return function() {
return Q(true)
.local(function(localData) {
localData.myVariable = 12;
});
};
};
module.exports = ClassA;
var ClassB = function() {};
ClassB.prototype.logVariable = function() {
return function() {
return Q(true)
.local(function(localData) {
console.log(localData.myVariable);
});
};
};
module.exports = ClassB;
var ClassA = require('./class-a'),
ClassB = require('./class-b');
function run2()
{
var myVariable,
a = new ClassA(),
b = new ClassB();
Q(true).then(a.setVariable()).then(b.logVariable());
}
Exposes the shared local data object to onAccess
function. The local data object will be passed
as the first argument of the function call.
function onAccess(localData) {}
Any changes to the properties of localData will be automatically shared with other
accessors. Note that changing the localData
object itself (e.g. localData = {};
)
will not have any effect outside of onAccess
function.
The return value of onAccess
will be treated as if it was promise.then(onAccess, null, null)
.
Execution order behaves the same way than promise.then(onAccess, null, null)
Changes in Q Local are copyright 2014-2015 Aleksi Asikainen.
Q is Copyright 2009–2015 Kristopher Michael Kowal.
MIT License (enclosed)