diff --git a/cli/src/main.rs b/cli/src/main.rs index 0d02d880894..a02527343aa 100644 --- a/cli/src/main.rs +++ b/cli/src/main.rs @@ -18,7 +18,7 @@ use boa_engine::{ optimizer::OptimizerOptions, script::Script, vm::flowgraph::{Direction, Graph}, - Context, JsError, Source, + Context, JsError, JsResult, Source, }; use boa_parser::source::ReadChar; use clap::{Parser, ValueEnum, ValueHint}; @@ -292,7 +292,7 @@ fn evaluate_file( ); let promise = module.load_link_evaluate(context); - context.run_jobs(); + context.run_jobs().map_err(|err| err.into_erased(context))?; let result = promise.state(); return match result { @@ -308,9 +308,9 @@ fn evaluate_file( Ok(v) => println!("{}", v.display()), Err(v) => eprintln!("Uncaught {v}"), } - context.run_jobs(); - - Ok(()) + context + .run_jobs() + .map_err(|err| err.into_erased(context).into()) } fn evaluate_files(args: &Opt, context: &mut Context, loader: &SimpleModuleLoader) { @@ -425,7 +425,9 @@ fn main() -> Result<()> { eprintln!("{}: {}", "Uncaught".red(), v.to_string().red()); } } - context.run_jobs(); + if let Err(err) = context.run_jobs() { + eprintln!("{err}"); + }; } } @@ -467,10 +469,10 @@ impl JobExecutor for Executor { } } - fn run_jobs(&self, context: &mut Context) { + fn run_jobs(&self, context: &mut Context) -> JsResult<()> { loop { if self.promise_jobs.borrow().is_empty() && self.async_jobs.borrow().is_empty() { - return; + return Ok(()); } let jobs = std::mem::take(&mut *self.promise_jobs.borrow_mut()); diff --git a/core/engine/src/builtins/promise/tests.rs b/core/engine/src/builtins/promise/tests.rs index 7f6c316fb93..e63fc131073 100644 --- a/core/engine/src/builtins/promise/tests.rs +++ b/core/engine/src/builtins/promise/tests.rs @@ -13,8 +13,7 @@ fn promise() { count += 1; "#}), TestAction::assert_eq("count", 2), - #[allow(clippy::redundant_closure_for_method_calls)] - TestAction::inspect_context(|ctx| ctx.run_jobs()), + TestAction::inspect_context(|ctx| ctx.run_jobs().unwrap()), TestAction::assert_eq("count", 3), ]); } diff --git a/core/engine/src/context/mod.rs b/core/engine/src/context/mod.rs index 3d5415e8b48..ccd57b4065c 100644 --- a/core/engine/src/context/mod.rs +++ b/core/engine/src/context/mod.rs @@ -477,9 +477,10 @@ impl Context { /// Runs all the jobs with the provided job executor. #[inline] - pub fn run_jobs(&mut self) { - self.job_executor().run_jobs(self); + pub fn run_jobs(&mut self) -> JsResult<()> { + let result = self.job_executor().run_jobs(self); self.clear_kept_objects(); + result } /// Asynchronously runs all the jobs with the provided job executor. @@ -490,11 +491,13 @@ impl Context { /// specific handling of each [`JobExecutor`]. If you want to execute jobs concurrently, you must /// provide a custom implementatin of `JobExecutor` to the context. #[allow(clippy::future_not_send)] - pub async fn run_jobs_async(&mut self) { - self.job_executor() + pub async fn run_jobs_async(&mut self) -> JsResult<()> { + let result = self + .job_executor() .run_jobs_async(&RefCell::new(self)) .await; self.clear_kept_objects(); + result } /// Abstract operation [`ClearKeptObjects`][clear]. diff --git a/core/engine/src/job.rs b/core/engine/src/job.rs index 30581a001cf..8838356b688 100644 --- a/core/engine/src/job.rs +++ b/core/engine/src/job.rs @@ -386,7 +386,7 @@ pub trait JobExecutor { fn enqueue_job(&self, job: Job, context: &mut Context); /// Runs all jobs in the executor. - fn run_jobs(&self, context: &mut Context); + fn run_jobs(&self, context: &mut Context) -> JsResult<()>; /// Asynchronously runs all jobs in the executor. /// @@ -395,7 +395,7 @@ pub trait JobExecutor { fn run_jobs_async<'a, 'b, 'fut>( &'a self, context: &'b RefCell<&mut Context>, - ) -> Pin + 'fut>> + ) -> Pin> + 'fut>> where 'a: 'fut, 'b: 'fut, @@ -427,7 +427,9 @@ pub struct IdleJobExecutor; impl JobExecutor for IdleJobExecutor { fn enqueue_job(&self, _: Job, _: &mut Context) {} - fn run_jobs(&self, _: &mut Context) {} + fn run_jobs(&self, _: &mut Context) -> JsResult<()> { + Ok(()) + } } /// A simple FIFO executor that bails on the first error. @@ -438,7 +440,7 @@ impl JobExecutor for IdleJobExecutor { /// To disable running promise jobs on the engine, see [`IdleJobExecutor`]. #[derive(Default)] pub struct SimpleJobExecutor { - jobs: RefCell>, + promise_jobs: RefCell>, async_jobs: RefCell>, } @@ -459,36 +461,38 @@ impl SimpleJobExecutor { impl JobExecutor for SimpleJobExecutor { fn enqueue_job(&self, job: Job, _: &mut Context) { match job { - Job::PromiseJob(p) => self.jobs.borrow_mut().push_back(p), + Job::PromiseJob(p) => self.promise_jobs.borrow_mut().push_back(p), Job::AsyncJob(a) => self.async_jobs.borrow_mut().push_back(a), } } - fn run_jobs(&self, context: &mut Context) { + fn run_jobs(&self, context: &mut Context) -> JsResult<()> { let context = RefCell::new(context); loop { let mut next_job = self.async_jobs.borrow_mut().pop_front(); while let Some(job) = next_job { - if pollster::block_on(job.call(&context)).is_err() { + if let Err(err) = pollster::block_on(job.call(&context)) { self.async_jobs.borrow_mut().clear(); - return; + self.promise_jobs.borrow_mut().clear(); + return Err(err); }; next_job = self.async_jobs.borrow_mut().pop_front(); } // Yeah, I have no idea why Rust extends the lifetime of a `RefCell` that should be immediately // dropped after calling `pop_front`. - let mut next_job = self.jobs.borrow_mut().pop_front(); + let mut next_job = self.promise_jobs.borrow_mut().pop_front(); while let Some(job) = next_job { - if job.call(&mut context.borrow_mut()).is_err() { - self.jobs.borrow_mut().clear(); - return; + if let Err(err) = job.call(&mut context.borrow_mut()) { + self.async_jobs.borrow_mut().clear(); + self.promise_jobs.borrow_mut().clear(); + return Err(err); }; - next_job = self.jobs.borrow_mut().pop_front(); + next_job = self.promise_jobs.borrow_mut().pop_front(); } - if self.async_jobs.borrow().is_empty() && self.jobs.borrow().is_empty() { - return; + if self.async_jobs.borrow().is_empty() && self.promise_jobs.borrow().is_empty() { + return Ok(()); } } } diff --git a/core/engine/src/object/builtins/jspromise.rs b/core/engine/src/object/builtins/jspromise.rs index 695c26cd9e3..a42370033cc 100644 --- a/core/engine/src/object/builtins/jspromise.rs +++ b/core/engine/src/object/builtins/jspromise.rs @@ -1143,14 +1143,14 @@ impl JsPromise { /// // Uncommenting the following line would panic. /// // context.run_jobs(); /// ``` - pub fn await_blocking(&self, context: &mut Context) -> Result { + pub fn await_blocking(&self, context: &mut Context) -> Result { loop { match self.state() { PromiseState::Pending => { - context.run_jobs(); + context.run_jobs()?; } PromiseState::Fulfilled(f) => break Ok(f), - PromiseState::Rejected(r) => break Err(r), + PromiseState::Rejected(r) => break Err(JsError::from_opaque(r)), } } } diff --git a/core/engine/src/tests/async_generator.rs b/core/engine/src/tests/async_generator.rs index 82889b1e264..54898589d13 100644 --- a/core/engine/src/tests/async_generator.rs +++ b/core/engine/src/tests/async_generator.rs @@ -47,7 +47,7 @@ fn return_on_then_infinite_loop() { }); g.return(); "#}), - TestAction::inspect_context(Context::run_jobs), + TestAction::inspect_context(|ctx| ctx.run_jobs().unwrap()), TestAction::assert_eq("count", 100), ]); } @@ -71,7 +71,7 @@ fn return_on_then_single() { }); let ret = g.return() "#}), - TestAction::inspect_context(Context::run_jobs), + TestAction::inspect_context(|ctx| ctx.run_jobs().unwrap()), TestAction::assert_eq("first", false), TestAction::assert_with_op("ret", |ret, context| { assert_promise_iter_value(&ret, &JsValue::undefined(), true, context); @@ -104,7 +104,7 @@ fn return_on_then_queue() { let second = g.next(); let ret = g.return(); "#}), - TestAction::inspect_context(Context::run_jobs), + TestAction::inspect_context(|ctx| ctx.run_jobs().unwrap()), TestAction::assert_with_op("first", |first, context| { assert_promise_iter_value(&first, &JsValue::from(1), false, context); true diff --git a/core/engine/src/tests/iterators.rs b/core/engine/src/tests/iterators.rs index eb341f1f986..2ddddd2fc50 100644 --- a/core/engine/src/tests/iterators.rs +++ b/core/engine/src/tests/iterators.rs @@ -46,8 +46,7 @@ fn iterator_close_in_continue_before_jobs() { actual.push("async fn end"); }(); "#}), - #[allow(clippy::redundant_closure_for_method_calls)] - TestAction::inspect_context(|ctx| ctx.run_jobs()), + TestAction::inspect_context(|ctx| ctx.run_jobs().unwrap()), TestAction::assert(indoc! {r#" arrayEquals( actual, @@ -110,8 +109,7 @@ fn async_iterator_close_in_continue_is_awaited() { actual.push("async fn end"); }(); "#}), - #[allow(clippy::redundant_closure_for_method_calls)] - TestAction::inspect_context(|ctx| ctx.run_jobs()), + TestAction::inspect_context(|ctx| ctx.run_jobs().unwrap()), TestAction::assert(indoc! {r#" arrayEquals( actual, @@ -198,8 +196,7 @@ fn mixed_iterators_close_in_continue() { actual.push("async fn end"); }(); "#}), - #[allow(clippy::redundant_closure_for_method_calls)] - TestAction::inspect_context(|ctx| ctx.run_jobs()), + TestAction::inspect_context(|ctx| ctx.run_jobs().unwrap()), TestAction::assert(indoc! {r#" arrayEquals( actual, diff --git a/core/engine/src/tests/promise.rs b/core/engine/src/tests/promise.rs index c8d64b8809e..a5f48f51521 100644 --- a/core/engine/src/tests/promise.rs +++ b/core/engine/src/tests/promise.rs @@ -31,7 +31,7 @@ fn issue_2658() { genTwo.next().then(v => { result2 = v; }); "# }), - TestAction::inspect_context(|ctx| ctx.run_jobs()), + TestAction::inspect_context(|ctx| ctx.run_jobs().unwrap()), TestAction::assert("!result1.done"), TestAction::assert_eq("result1.value", 5), TestAction::assert("!result2.done"), diff --git a/core/engine/tests/imports.rs b/core/engine/tests/imports.rs index 36def275947..a4feca921d4 100644 --- a/core/engine/tests/imports.rs +++ b/core/engine/tests/imports.rs @@ -23,7 +23,7 @@ fn subdirectories() { let module = boa_engine::Module::parse(source, None, &mut context).unwrap(); let result = module.load_link_evaluate(&mut context); - context.run_jobs(); + context.run_jobs().unwrap(); match result.state() { PromiseState::Pending => {} PromiseState::Fulfilled(v) => { diff --git a/core/engine/tests/module.rs b/core/engine/tests/module.rs index 09ec3522588..93db4609812 100644 --- a/core/engine/tests/module.rs +++ b/core/engine/tests/module.rs @@ -41,7 +41,7 @@ fn test_json_module_from_str() { let module = Module::parse(source, None, &mut context).unwrap(); let promise = module.load_link_evaluate(&mut context); - context.run_jobs(); + context.run_jobs().unwrap(); match promise.state() { PromiseState::Pending => {} diff --git a/core/interop/src/lib.rs b/core/interop/src/lib.rs index 436680d29db..e3b4bff3745 100644 --- a/core/interop/src/lib.rs +++ b/core/interop/src/lib.rs @@ -565,7 +565,7 @@ fn into_js_module() { let root_module = Module::parse(source, None, &mut context).unwrap(); let promise_result = root_module.load_link_evaluate(&mut context); - context.run_jobs(); + context.run_jobs().unwrap(); // Checking if the final promise didn't return an error. assert!( @@ -617,7 +617,7 @@ fn can_throw_exception() { let root_module = Module::parse(source, None, &mut context).unwrap(); let promise_result = root_module.load_link_evaluate(&mut context); - context.run_jobs(); + context.run_jobs().unwrap(); // Checking if the final promise didn't return an error. assert_eq!( @@ -721,7 +721,7 @@ fn class() { let root_module = Module::parse(source, None, &mut context).unwrap(); let promise_result = root_module.load_link_evaluate(&mut context); - context.run_jobs(); + context.run_jobs().unwrap(); // Checking if the final promise didn't return an error. assert!( diff --git a/core/interop/src/macros.rs b/core/interop/src/macros.rs index 9be07ef765d..5fe2d585052 100644 --- a/core/interop/src/macros.rs +++ b/core/interop/src/macros.rs @@ -525,7 +525,7 @@ fn js_class_test() { let root_module = Module::parse(source, None, &mut context).unwrap(); let promise_result = root_module.load_link_evaluate(&mut context); - context.run_jobs(); + context.run_jobs().unwrap(); // Checking if the final promise didn't return an error. assert!( diff --git a/core/interop/tests/embedded.rs b/core/interop/tests/embedded.rs index 04b35a9f6ad..d0a8501d65e 100644 --- a/core/interop/tests/embedded.rs +++ b/core/interop/tests/embedded.rs @@ -35,7 +35,7 @@ fn simple() { ) .expect("failed to parse module"); let promise = module.load_link_evaluate(&mut context); - context.run_jobs(); + context.run_jobs().unwrap(); match promise.state() { PromiseState::Fulfilled(value) => { diff --git a/examples/src/bin/module_fetch_async.rs b/examples/src/bin/module_fetch_async.rs index 458d70f6d13..abc7c90e982 100644 --- a/examples/src/bin/module_fetch_async.rs +++ b/examples/src/bin/module_fetch_async.rs @@ -124,7 +124,7 @@ fn main() -> JsResult<()> { // Important to call `Context::run_jobs`, or else all the futures and promises won't be // pushed forward by the job queue. - context.run_jobs(); + context.run_jobs()?; match promise.state() { // Our job queue guarantees that all promises and futures are finished after returning @@ -204,15 +204,15 @@ impl JobExecutor for Queue { } // While the sync flavor of `run_jobs` will block the current thread until all the jobs have finished... - fn run_jobs(&self, context: &mut Context) { - smol::block_on(smol::LocalExecutor::new().run(self.run_jobs_async(&RefCell::new(context)))); + fn run_jobs(&self, context: &mut Context) -> JsResult<()> { + smol::block_on(smol::LocalExecutor::new().run(self.run_jobs_async(&RefCell::new(context)))) } // ...the async flavor won't, which allows concurrent execution with external async tasks. fn run_jobs_async<'a, 'b, 'fut>( &'a self, context: &'b RefCell<&mut Context>, - ) -> Pin + 'fut>> + ) -> Pin> + 'fut>> where 'a: 'fut, 'b: 'fut, @@ -220,7 +220,7 @@ impl JobExecutor for Queue { Box::pin(async move { // Early return in case there were no jobs scheduled. if self.promise_jobs.borrow().is_empty() && self.async_jobs.borrow().is_empty() { - return; + return Ok(()); } let mut group = FutureGroup::new(); loop { @@ -231,7 +231,7 @@ impl JobExecutor for Queue { if self.promise_jobs.borrow().is_empty() { let Some(result) = group.next().await else { // Both queues are empty. We can exit. - return; + return Ok(()); }; if let Err(err) = result { diff --git a/examples/src/bin/modules.rs b/examples/src/bin/modules.rs index 68037590f3e..eec64471c1d 100644 --- a/examples/src/bin/modules.rs +++ b/examples/src/bin/modules.rs @@ -87,7 +87,7 @@ fn main() -> Result<(), Box> { ); // Very important to push forward the job queue after queueing promises. - context.run_jobs(); + context.run_jobs()?; // Checking if the final promise didn't return an error. match promise_result.state() { diff --git a/examples/src/bin/smol_event_loop.rs b/examples/src/bin/smol_event_loop.rs index 5d4998654cb..749be95444c 100644 --- a/examples/src/bin/smol_event_loop.rs +++ b/examples/src/bin/smol_event_loop.rs @@ -21,14 +21,14 @@ use smol::{future, stream::StreamExt}; // This example shows how to create an event loop using the smol runtime. // The example contains two "flavors" of event loops: -fn main() { +fn main() -> JsResult<()> { // An internally async event loop. This event loop blocks the execution of the thread // while executing tasks, but internally uses async to run its tasks. - internally_async_event_loop(); + internally_async_event_loop()?; // An externally async event loop. This event loop can yield to the runtime to concurrently // run tasks with it. - externally_async_event_loop(); + externally_async_event_loop() } // Taken from the `smol_event_loop.rs` example. @@ -66,15 +66,15 @@ impl JobExecutor for Queue { } // While the sync flavor of `run_jobs` will block the current thread until all the jobs have finished... - fn run_jobs(&self, context: &mut Context) { - smol::block_on(smol::LocalExecutor::new().run(self.run_jobs_async(&RefCell::new(context)))); + fn run_jobs(&self, context: &mut Context) -> JsResult<()> { + smol::block_on(smol::LocalExecutor::new().run(self.run_jobs_async(&RefCell::new(context)))) } // ...the async flavor won't, which allows concurrent execution with external async tasks. fn run_jobs_async<'a, 'b, 'fut>( &'a self, context: &'b RefCell<&mut Context>, - ) -> Pin + 'fut>> + ) -> Pin> + 'fut>> where 'a: 'fut, 'b: 'fut, @@ -82,7 +82,7 @@ impl JobExecutor for Queue { Box::pin(async move { // Early return in case there were no jobs scheduled. if self.promise_jobs.borrow().is_empty() && self.async_jobs.borrow().is_empty() { - return; + return Ok(()); } let mut group = FutureGroup::new(); loop { @@ -93,7 +93,7 @@ impl JobExecutor for Queue { if self.promise_jobs.borrow().is_empty() { let Some(result) = group.next().await else { // Both queues are empty. We can exit. - return; + return Ok(()); }; if let Err(err) = result { @@ -230,7 +230,7 @@ const SCRIPT: &str = r" // This flavor is most recommended when you have an application that: // - Needs to wait until the engine finishes executing; depends on the execution result to continue. // - Delegates the execution of the application to the engine's event loop. -fn internally_async_event_loop() { +fn internally_async_event_loop() -> JsResult<()> { println!("====== Internally async event loop. ======"); // Initialize the queue and the context @@ -249,15 +249,16 @@ fn internally_async_event_loop() { // Important to run this after evaluating, since this is what triggers to run the enqueued jobs. println!("Running jobs..."); - context.run_jobs(); + context.run_jobs()?; println!("Total elapsed time: {:?}\n", now.elapsed()); + Ok(()) } // This flavor is most recommended when you have an application that: // - Cannot afford to block until the engine finishes executing. // - Needs to process IO requests between executions that will be consumed by the engine. -fn externally_async_event_loop() { +fn externally_async_event_loop() -> JsResult<()> { println!("====== Externally async event loop. ======"); let executor = smol::Executor::new(); @@ -282,7 +283,7 @@ fn externally_async_event_loop() { interval.next().await; println!("Executed interval tick {i}"); } - println!("Finished smol interval job...") + println!("Finished smol interval job..."); }); let engine = async { @@ -295,11 +296,13 @@ fn externally_async_event_loop() { // Run the jobs asynchronously, which avoids blocking the main thread. println!("Running jobs..."); - context.run_jobs_async().await; + context.run_jobs_async().await }; - future::zip(counter, engine).await; + future::zip(counter, engine).await.1?; println!("Total elapsed time: {:?}\n", now.elapsed()); - })); + + Ok(()) + })) } diff --git a/examples/src/bin/synthetic.rs b/examples/src/bin/synthetic.rs index e38562cde4b..43d4955259c 100644 --- a/examples/src/bin/synthetic.rs +++ b/examples/src/bin/synthetic.rs @@ -62,7 +62,7 @@ fn main() -> Result<(), Box> { let promise_result = module.load_link_evaluate(context); // Very important to push forward the job queue after queueing promises. - context.run_jobs(); + context.run_jobs()?; // Checking if the final promise didn't return an error. match promise_result.state() { diff --git a/examples/src/bin/tokio_event_loop.rs b/examples/src/bin/tokio_event_loop.rs index 07b187ce48d..0c83887c229 100644 --- a/examples/src/bin/tokio_event_loop.rs +++ b/examples/src/bin/tokio_event_loop.rs @@ -22,14 +22,14 @@ use tokio::{task, time}; // This example shows how to create an event loop using the tokio runtime. // The example contains two "flavors" of event loops: -fn main() { +fn main() -> JsResult<()> { // An internally async event loop. This event loop blocks the execution of the thread // while executing tasks, but internally uses async to run its tasks. - internally_async_event_loop(); + internally_async_event_loop()?; // An externally async event loop. This event loop can yield to the runtime to concurrently // run tasks with it. - externally_async_event_loop(); + externally_async_event_loop() } /// An event queue using tokio to drive futures to completion. @@ -66,20 +66,20 @@ impl JobExecutor for Queue { } // While the sync flavor of `run_jobs` will block the current thread until all the jobs have finished... - fn run_jobs(&self, context: &mut Context) { + fn run_jobs(&self, context: &mut Context) -> JsResult<()> { let runtime = tokio::runtime::Builder::new_current_thread() .enable_time() .build() .unwrap(); - task::LocalSet::default().block_on(&runtime, self.run_jobs_async(&RefCell::new(context))); + task::LocalSet::default().block_on(&runtime, self.run_jobs_async(&RefCell::new(context))) } // ...the async flavor won't, which allows concurrent execution with external async tasks. fn run_jobs_async<'a, 'b, 'fut>( &'a self, context: &'b RefCell<&mut Context>, - ) -> Pin + 'fut>> + ) -> Pin> + 'fut>> where 'a: 'fut, 'b: 'fut, @@ -87,7 +87,7 @@ impl JobExecutor for Queue { Box::pin(async move { // Early return in case there were no jobs scheduled. if self.promise_jobs.borrow().is_empty() && self.async_jobs.borrow().is_empty() { - return; + return Ok(()); } let mut group = FutureGroup::new(); loop { @@ -98,7 +98,7 @@ impl JobExecutor for Queue { if self.promise_jobs.borrow().is_empty() { let Some(result) = group.next().await else { // Both queues are empty. We can exit. - return; + return Ok(()); }; if let Err(err) = result { @@ -238,7 +238,7 @@ const SCRIPT: &str = r" // This flavor is most recommended when you have an application that: // - Needs to wait until the engine finishes executing; depends on the execution result to continue. // - Delegates the execution of the application to the engine's event loop. -fn internally_async_event_loop() { +fn internally_async_event_loop() -> JsResult<()> { println!("====== Internally async event loop. ======"); // Initialize the queue and the context @@ -257,16 +257,18 @@ fn internally_async_event_loop() { // Important to run this after evaluating, since this is what triggers to run the enqueued jobs. println!("Running jobs..."); - context.run_jobs(); + context.run_jobs()?; println!("Total elapsed time: {:?}\n", now.elapsed()); + + Ok(()) } // This flavor is most recommended when you have an application that: // - Cannot afford to block until the engine finishes executing. // - Needs to process IO requests between executions that will be consumed by the engine. #[tokio::main] -async fn externally_async_event_loop() { +async fn externally_async_event_loop() -> JsResult<()> { println!("====== Externally async event loop. ======"); // Initialize the queue and the context let queue = Queue::new(); @@ -281,15 +283,19 @@ async fn externally_async_event_loop() { let now = Instant::now(); // Example of an asynchronous workload that must be run alongside the engine. - let counter = tokio::spawn(async { - let mut interval = time::interval(Duration::from_millis(100)); - println!("Starting tokio interval job..."); - for i in 0..10 { - interval.tick().await; - println!("Executed interval tick {i}"); - } - println!("Finished tokio interval job...") - }); + let counter = async { + tokio::spawn(async { + let mut interval = time::interval(Duration::from_millis(100)); + println!("Starting tokio interval job..."); + for i in 0..10 { + interval.tick().await; + println!("Executed interval tick {i}"); + } + println!("Finished tokio interval job...") + }) + .await + .map_err(|err| JsNativeError::typ().with_message(err.to_string()).into()) + }; let local_set = &mut task::LocalSet::default(); let engine = local_set.run_until(async { @@ -302,11 +308,12 @@ async fn externally_async_event_loop() { // Run the jobs asynchronously, which avoids blocking the main thread. println!("Running jobs..."); - context.run_jobs_async().await; - Ok(()) + context.run_jobs_async().await }); - tokio::try_join!(counter, engine).unwrap(); + tokio::try_join!(counter, engine)?; println!("Total elapsed time: {:?}\n", now.elapsed()); + + Ok(()) } diff --git a/tests/tester/src/exec/mod.rs b/tests/tester/src/exec/mod.rs index e5ce0553cfd..6bca5c1f163 100644 --- a/tests/tester/src/exec/mod.rs +++ b/tests/tester/src/exec/mod.rs @@ -289,7 +289,9 @@ impl Test { let promise = module.load_link_evaluate(context); - context.run_jobs(); + if let Err(err) = context.run_jobs() { + return (false, format!("Uncaught {err}")); + }; match promise.state() { PromiseState::Pending => { @@ -322,7 +324,9 @@ impl Test { } }; - context.run_jobs(); + if let Err(err) = context.run_jobs() { + return (false, format!("Uncaught {err}")); + }; match *async_result.inner.borrow() { UninitResult::Err(ref e) => return (false, format!("Uncaught {e}")), @@ -388,7 +392,9 @@ impl Test { let promise = module.load(context); - context.run_jobs(); + if let Err(err) = context.run_jobs() { + return (false, format!("Uncaught {err}")); + }; match promise.state() { PromiseState::Pending => { @@ -433,7 +439,9 @@ impl Test { let promise = module.load(context); - context.run_jobs(); + if let Err(err) = context.run_jobs() { + return (false, format!("Uncaught {err}")); + }; match promise.state() { PromiseState::Pending => { @@ -451,7 +459,9 @@ impl Test { let promise = module.evaluate(context); - context.run_jobs(); + if let Err(err) = context.run_jobs() { + return (false, format!("Uncaught {err}")); + }; match promise.state() { PromiseState::Pending => {