1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
//! This example demonstrates how to use `any()`/`all()`
//! in different ways for combining vector of promises
//! and react to result when all/any of the passed
//! promises got resolved.
use bevy::prelude::*;
use pecs::prelude::*;
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_plugins(PecsPlugin)
.add_systems(Startup, setup)
.run();
}
const URLS: &[&'static str] = &["https://google.com", "https://bevyengine.org", "https://github.com"];
fn setup(mut commands: Commands) {
commands.add(
// This is how Promise::all works in general:
Promise::start(asyn!(_ => {
info!("Requesting all {} urls", URLS.len());
// requests is a vec of promises
// requests: Vec<Promise<&&str, Result<Response, String>>>
// &str is the state (came from .with(url) call)
// Result<Response, String> is the http response/error
let requests = URLS
.into_iter()
.map(|url| asyn::http::get(url).send().with(url))
.collect::<Vec<_>>();
// Promise::all() takes a vec of promises and returns
// other promise that will be resolved when all promises
// from vec got resolved.
Promise::all(requests)
}))
// It will be resolved with Vec<(&&str, Result<Response, String>)>:
// the vector of (state, result) from promises you did
// pass to Promise::all() in previous call
.then(asyn!(_, resolved => {
info!("Responses:");
for (url, result) in resolved.iter() {
match result {
Ok(response) => info!(" [{}] {url}", response.status),
Err(error) => info!(" Can't get {url}: {error}")
}
}
Promise::pass()
}))
// Promise::any() works the same way: it takes a vec of promises
// and returns another promise that will be resolved when the ANY
// of provided promises got resolved:
.then(asyn!({
info!("Requesting any of {} urls", URLS.len());
let requests = URLS
.into_iter()
.map(|url| asyn::http::get(url).send().with(url))
.collect::<Vec<_>>();
Promise::any(requests)
}))
// resolved here is (&&str, Result<Response, String),
// the result of first reolved promise passed to
// Promise::any() at previous call
.then(asyn!(_, (url, result) => {
match result {
Ok(response) => info!("{url} was fastest with status {}", response.status),
Err(error) => info!("{url} failed first with {error}")
}
Promise::pass()
}))
// `pecs` comes with iterator extension for promises.
// It allows you to make the same calls, but sligtly simpler:
.then(asyn!(_, _ => {
info!("Requesting all urls using iterator extension");
// Instead of creating Vec<Promise> manually and pass it
// to Promise:all(), you can call .promise().all() on
// any Iterator<Item =Promise>
URLS
.into_iter()
.map(|url| asyn::http::get(url).send().with(url))
.promise()
.all()
}))
.then(asyn!(_, resolved => {
info!("Responses:");
for (url, result) in resolved.iter() {
match result {
Ok(response) => info!(" [{}] {url}", response.status),
Err(error) => info!(" Can't get {url}: {error}")
}
}
Promise::pass()
}))
// and the same way .prmise().any() could be done:
.then(asyn!(_, _ => {
info!("Requesting any of urls using iterator extension");
URLS
.into_iter()
.map(|url| asyn::http::get(url).send().with(url))
.promise()
.any()
}))
.then(asyn!(_, (url, result) => {
match result {
Ok(response) => info!("{url} was fastest with status {}", response.status),
Err(error) => info!("{url} failed first with {error}")
}
Promise::pass()
}))
// In previouse calls state (the first argument to asyn!) was ignored,
// () was passed between calls.
// Let's try to report how long it is takes to get all requests and
// the fastest one.
// To do it, you can store current time and calculate the difference in
// the next call.
// To get the time you can use bevy Time resource
.then(asyn!(state, _, time: Res<Time> => {
info!("Tracking time to get response from all requests");
let started_at = time.elapsed_seconds();
let requests = URLS
.into_iter()
.map(|url| asyn::http::get(url).send())
.collect::<Vec<_>>();
state
// change the state value
.with(started_at)
// .all() method of the PromiseState takes same
// arguments as PromiseAll and does the same job
// but also bypys the state to the next calls
.all(requests)
}))
// resolved value doesn't important here
// state is more interesting: its type is
// PromiseState<f32>
.then(asyn!(state, _, time: Res<Time> => {
let current_time = time.elapsed_seconds();
let delta = current_time - state.value;
info!("Time to complete all requests: {delta:0.3}");
// make new call right here.
info!("Tracking time to get response from the fastest request");
// store current time to make proper calculations after resolve
state.value = current_time;
let requests = URLS
.into_iter()
.map(|url| asyn::http::get(url).send())
.collect::<Vec<_>>();
state.any(requests)
}))
.then(asyn!(state, _, time: Res<Time> => {
let current_time = time.elapsed_seconds();
let delta = current_time - state.value;
info!("Time to complete fastest request: {delta:0.3}");
state.value = current_time;
// If you want to use iterator extension method
// you need to pass context manually.
info!("Tracking one more time the fastest one");
URLS
.into_iter()
.map(|url| asyn::http::get(url).send())
.promise()
.any()
.with(state.value)
}))
.then(asyn!(state, _, time: Res<Time> => {
let current_time = time.elapsed_seconds();
let delta = current_time - state.value;
info!("Time to complete fastest request: {delta:0.3}");
state.pass()
}))
// close app at the end
.then(asyn!(_, _ => {
info!("See you!");
asyn::app::exit()
})),
);
}