Futures are mainly used for modules, but they can also be used to run some code at some later time.
A future does not require a change. If the future is followed with a then
or else
closure, then the
code inside this closure will generate it’s own event if required.
For example, the code below will always create a change, no matter what the value of x
is. This is because ThingsDB
has to know if a change is required before it knows the value of x
.
if (x > 10) {
.answers.push(x);
};
When using a future we could optimize the code:
if (x > 10) {
future(nil, x).then(|_, x| {
.answers.push(x); // This will still require a *change*, but the event
// is only created when x > 10.
});
};
Instead of using the future(nil, ..).then(|_, ..| ..)
construction, a future accepts a closure as first argument to be used as a shortcut. So the above can be written as:
if (x > 10) {
future(|x| {
.answers.push(x); // This will still require a *change*, but the event
// is only created when x > 10.
});
};
Arguments may be provided by a list. In this case the length of the list must match the number of arguments accepted by the closure:
future(|x| {
.answers.push(x); // x = 10
}, [10]);
Arguments must not contain stored data with an Id. Such an argument must be provided using the Id as ThingsDB otherwise has no way of knowing that the object with Id still exists in the context when using the object.
.x = {};
future(|x| {
.answers.push(x);
}, [.x]); // !!ERROR: context does not allow arguments which are stored by Id
Parse such a thing by using the plain Id:
future(|x_id| {
x = thing(x_id);
.answers.push(x);
}, [.x.id()]); // Parse the plain Id
When a future is used to call a module, the first argument of the future will be the request for the module and must be a thing containing at least a module
property.
For example, the code below will trigger the module DEMO
. The module would receive {module: "DEMO"}
as request.
future({
module: 'DEMO'
});
Besides the required module
property, a property deep
will be understood and will tell ThingsDB how deep the request must be packed. The default deep value is the current deep value.
For example:
// We can be explicit with deep and then must use at least 2, otherwise the items are not packed for the module request
future({
module: 'DEMO',
deep: 2,
items: [{
name: 'item1'
}, {
name: 'item2'
}]
});
Data from a module is returned to ThingsDB as mpdata by default. For example:
// Suppose we have a DEMO module which accepts a message and returns with a reply message:
future({
module: 'DEMO',
message: 'This is a test'
}).then(|reply| type(reply)); // type reply will be `mpdata`, not `str` !!
The above is fine if we want the result, in this case the reply
, back to our client. If we on the other hand want to do
something with the reply
message, we can either choose to call .load() on the reply
value but
a better option would be to set the load
property to true
in the request, for example:
// Suppose we have a DEMO module which accepts a message and returns with a reply message:
future({
module: 'DEMO',
load: true,
message: 'This is a test'
}).then(|reply| type(reply)); // now `reply` is of type `str`
Function | Description |
---|---|
then | Accepts a closure which will be executed when the future was successful. |
else | Accepts a closure which will be executed when the future has failed. |
Function | Description |
---|---|
future | Create a new future. |
is_future | Test if a given value is of type future. |