Expand description
Rust crate to help perform deferred execution of code logic.
Problems that deferred
crate helps to solve
Probably at some point in your project you will want to make a function that can have partitioned logic and you want to call each of that parts at some strictly defined time specified by you.
fn foo(v: i32) -> Deferred<i32> {
deferred!(v, [
|c| state!(c.state() + 1),
|c| foo2(c.state()).into(),
|c| state!(c.state() + 2)
])
}
fn foo2(v: i32) -> Deferred<i32> {
deferred!(v, [
|c| state!(c.state() * 2),
|c| state!(c.state() * 3)
])
}
let d = foo(1);
assert_eq!(d.state(), Some(&1));
let d = d.resume().unwrap();
assert_eq!(d.state(), Some(&2));
let d = d.resume().unwrap();
assert_eq!(d.state(), Some(&4));
let d = d.resume().unwrap();
assert_eq!(d.state(), Some(&12));
let d = d.resume().unwrap();
assert_eq!(d.state(), Some(&14));
assert_eq!(d.can_resume(), false);
You can think of it as staticaly defined Promise
-like abstraction known in JavaScript or
other languages with high abstraction of deferred code execution.
It is not based on threads
Main reason that this crate was created is that when you work with WASM target, you cannot use
Futures
or threads but you still need to run some of your code asynchronously, most likely
execute heavy/long calculations “in background” and you cannot make browser freeze.
Need to use undefined state type? Look, there is Value
wrapper!
Sometimes you cannot have the same context input and output types, for example:
fn foo(v: i32) -> Deferred<String> {
deferred!(v, [
|c| state!(c.state() + 1),
|c| state!(format!("{}", c.state()))
])
}
let result: String = foo(42).consume();
Code above gets i32
as input and expects that at the end we get String
value and it does
not compile. You could solve it by making tuple with options of each types used in context
inputs and outputs, like this:
type State = (Option<i32>, Option<String>);
fn foo(v: i32) -> Deferred<State> {
deferred!((Some(v), None), [
|c| state!((Some(c.state().0.unwrap() + 1), None)),
|c| state!((None, Some(format!("{}", c.state().0.unwrap()))))
])
}
let result = foo(41).consume().1.unwrap();
assert_eq!(&result, "42");
but this looks ugly and gets even worse when you have much much more types to use - we do not
want that. We can use Value
type which is basically a boxed wrapper of any value (that means:
you have to deal with a little runtime overhead because of storing and accessing value on heap).
Here is how to use Value
as state:
fn foo(v: i32) -> Deferred<Value> {
deferred!(value!(v), [
|c| state!(value!(c.state().consume::<i32>() + 1)),
|c| state!(value!(format!("{}", c.state().consume::<i32>())))
])
}
let result = foo(41).consume().consume::<String>();
assert_eq!(&result, "42");
Re-exports
pub use crate::context::*;
pub use crate::deferred::*;
pub use crate::deferred_manager::*;
pub use crate::value::*;