quickjs_runtime/jsutils/promises.rs
1use crate::jsutils::helper_tasks::{add_helper_task, add_helper_task_async};
2use crate::jsutils::JsError;
3use crate::quickjs_utils::promises::QuickJsPromiseAdapter;
4use crate::quickjsrealmadapter::QuickJsRealmAdapter;
5use crate::quickjsvalueadapter::QuickJsValueAdapter;
6use futures::Future;
7
8#[allow(clippy::type_complexity)]
9/// create a new promise with a producer and a mapper
10/// the producer will run in a helper thread(in the tokio thread pool) and thus get a result asynchronously
11/// the resulting value will then be mapped to a JSValueRef by the mapper in the EventQueue thread
12/// the promise which was returned is then resolved with the value which is returned by the mapper
13pub fn new_resolving_promise<P, R, M>(
14 realm: &QuickJsRealmAdapter,
15 producer: P,
16 mapper: M,
17) -> Result<QuickJsValueAdapter, JsError>
18where
19 R: Send + 'static,
20 P: FnOnce() -> Result<R, JsError> + Send + 'static,
21 M: FnOnce(&QuickJsRealmAdapter, R) -> Result<QuickJsValueAdapter, JsError> + Send + 'static,
22{
23 // create promise
24 let promise_ref = realm.create_promise()?;
25 let return_ref = promise_ref.js_promise_get_value(realm);
26
27 // add to map and keep id
28 let id = realm.cache_promise(promise_ref);
29
30 let rti_ref = realm.get_runtime_facade_inner();
31
32 let realm_id = realm.get_realm_id().to_string();
33 // go async
34 add_helper_task(move || {
35 // in helper thread, produce result
36 let produced_result = producer();
37 if let Some(rti) = rti_ref.upgrade() {
38 rti.add_rt_task_to_event_loop_void(move |rt| {
39 if let Some(realm) = rt.get_realm(realm_id.as_str()) {
40 // in q_js_rt worker thread, resolve promise
41 // retrieve promise
42 let prom_ref_opt: Option<QuickJsPromiseAdapter> =
43 realm.consume_cached_promise(id);
44 if let Some(prom_ref) = prom_ref_opt {
45 //let prom_ref = realm.js_promise_cache_consume(id);
46 match produced_result {
47 Ok(ok_res) => {
48 // map result to JSValueRef
49 let raw_res = mapper(realm, ok_res);
50
51 // resolve or reject promise
52 match raw_res {
53 Ok(val_ref) => {
54 if let Err(e) = prom_ref.js_promise_resolve(realm, &val_ref)
55 {
56 log::error!(
57 "[{}] could not resolve promise5: {}",
58 realm.get_realm_id(),
59 e
60 );
61 }
62 }
63 Err(err) => {
64 let err_ref = realm
65 .create_error(
66 err.get_name(),
67 err.get_message(),
68 err.get_stack(),
69 )
70 .expect("could not create error");
71 if let Err(e) = prom_ref.js_promise_reject(realm, &err_ref)
72 {
73 log::error!(
74 "[{}] could not reject promise4: {}",
75 realm.get_realm_id(),
76 e
77 );
78 }
79 }
80 }
81 }
82 Err(err) => {
83 // todo use error:new_error(err)
84 let err_ref = realm
85 .create_error(
86 err.get_name(),
87 err.get_message(),
88 err.get_stack(),
89 )
90 .expect("could not create error");
91 if let Err(e) = prom_ref.js_promise_reject(realm, &err_ref) {
92 log::error!(
93 "[{}] could not reject promise3: {}",
94 realm.get_realm_id(),
95 e
96 );
97 }
98 }
99 }
100 } else {
101 log::error!(
102 "async promise running for dropped realm: {} promise_id:{}",
103 realm_id,
104 id
105 );
106 }
107 } else {
108 log::error!("async promise running for dropped realm: {}", realm_id);
109 }
110 });
111 } else {
112 log::error!("async promise running for dropped runtime");
113 }
114 });
115
116 Ok(return_ref)
117}
118
119#[allow(clippy::type_complexity)]
120/// create a new promise with an async producer and a mapper
121/// the producer will be awaited asynchronously and
122/// the resulting value will then be mapped to a JSValueRef by the mapper in the EventQueue thread
123/// the promise which was returned is then resolved with the value which is returned by the mapper
124pub(crate) fn new_resolving_promise_async<P, R, M>(
125 realm: &QuickJsRealmAdapter,
126 producer: P,
127 mapper: M,
128) -> Result<QuickJsValueAdapter, JsError>
129where
130 R: Send + 'static,
131 P: Future<Output = Result<R, JsError>> + Send + 'static,
132 M: FnOnce(&QuickJsRealmAdapter, R) -> Result<QuickJsValueAdapter, JsError> + Send + 'static,
133{
134 // create promise
135 let promise_ref = realm.create_promise()?;
136 let return_ref = promise_ref.js_promise_get_value(realm);
137
138 // add to map and keep id
139 let id = realm.cache_promise(promise_ref);
140
141 let rti_ref = realm.get_runtime_facade_inner();
142
143 let realm_id = realm.get_realm_id().to_string();
144 // go async
145 let _ignore_result = add_helper_task_async(async move {
146 // in helper thread, produce result
147 let produced_result = producer.await;
148 if let Some(rti) = rti_ref.upgrade() {
149 rti.add_rt_task_to_event_loop_void(move |rt| {
150 if let Some(realm) = rt.get_realm(realm_id.as_str()) {
151 // in q_js_rt worker thread, resolve promise
152 // retrieve promise
153 let prom_ref_opt: Option<QuickJsPromiseAdapter> =
154 realm.consume_cached_promise(id);
155 if let Some(prom_ref) = prom_ref_opt {
156 //let prom_ref = realm.js_promise_cache_consume(id);
157 match produced_result {
158 Ok(ok_res) => {
159 // map result to JSValueRef
160 let raw_res = mapper(realm, ok_res);
161
162 // resolve or reject promise
163 match raw_res {
164 Ok(val_ref) => {
165 if let Err(e) = prom_ref.js_promise_resolve(realm, &val_ref)
166 {
167 log::error!(
168 "[{}] could not resolve promise: {}",
169 realm.get_realm_id(),
170 e
171 );
172 }
173 }
174 Err(err) => {
175 let err_ref = realm
176 .create_error(
177 err.get_name(),
178 err.get_message(),
179 err.get_stack(),
180 )
181 .expect("could not create err");
182 if let Err(e) = prom_ref.js_promise_reject(realm, &err_ref)
183 {
184 log::error!(
185 "[{}] could not reject promise: {}",
186 realm.get_realm_id(),
187 e
188 );
189 }
190 }
191 }
192 }
193 Err(err) => {
194 // todo use error:new_error(err)
195 let err_ref = realm
196 .create_error(
197 err.get_name(),
198 err.get_message(),
199 err.get_stack(),
200 )
201 .expect("could not create str");
202 if let Err(e) = prom_ref.js_promise_reject(realm, &err_ref) {
203 log::error!(
204 "[{}] could not reject promise2: {}",
205 realm.get_realm_id(),
206 e
207 );
208 }
209 }
210 }
211 } else {
212 log::error!(
213 "async promise running on dropped realm: {} promise_id:{}",
214 realm_id,
215 id
216 );
217 }
218 } else {
219 log::error!("async promise running on dropped realm: {}", realm_id);
220 }
221 });
222 } else {
223 log::error!("async promise running on dropped runtime");
224 }
225 });
226 Ok(return_ref)
227}