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}