Q with local data context
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.
Why not use function scope?
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)
});
});
}
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;
});
};
};
module.exports = ClassA;
var ClassB = function() {};
ClassB.prototype.logVariable = function() {
return function() {
return Q(true)
.then(function() {
console.log(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());
}
Since setVariable()
and logVariable()
do not share context with run()
,
myVariable
will remain undefined within ClassA
and ClassB
.
Why not pass variables as function arguments?
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.
Is there a better approach?
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.
Setting values
Q(true)
.local(function(localData) {
localData.myVariable = 1001;
});
Getting values
Q(true)
.local(function(localData) {
console.log(localData.myVariable);
});
Previous examples in local data flavor
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);
});
});
}
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());
}
API Reference
promise.local(onAccess)
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)
License
Changes in Q Local are copyright 2014-2015 Aleksi Asikainen.
Q is Copyright 2009–2015 Kristopher Michael Kowal.
MIT License (enclosed)