Repository: awslabs/llrt
File: src/events.rs (Rust)

Code before:
    pub fn set_reason(&mut self, reason: Opt<Value<'js>>) -> Result<Option<Value<'js>>> {
        if let Some(new_reason) = reason.0 {
            Ok(self.reason.replace(new_reason))
        } else {
            let old_reason = self.reason.take();
            Ok(old_reason)
        }
    }

    #[qjs(skip)]
    pub fn set_aborted(&mut self, is_aborted: bool) {
        self.aborted = is_aborted;
    }

    #[qjs(static)]
    pub fn abort(reason: Opt<Value<'js>>) -> AbortSignal {
        AbortSignal {
            aborted: true,
            reason: reason.0,
        }
    }

    // TODO: Returns an AbortSignal that will automatically abort after a specified time.
    #[qjs(static)]
    pub fn timeout() -> AbortSignal<'js> {
        todo!()
    }
}

pub struct EventsModule;

impl ModuleDef for EventsModule {
    fn declare(declare: &mut Declarations) -> Result<()> {
        declare.declare(stringify!(EventEmitter))?;
        declare.declare("default")?;

        Ok(())
    }

    fn evaluate<'js>(ctx: &Ctx<'js>, exports: &mut Exports<'js>) -> Result<()> {
        let ctor = Class::<EventEmitter>::create_constructor(ctx)?
            .expect("Can't create EventEmitter constructor");
        ctor.set(stringify!(EventEmitter), ctor.clone())?;
        exports.export(stringify!(EventEmitter), ctor.clone())?;
        exports.export("default", ctor)?;

        EventEmitter::add_event_emitter_prototype(ctx)?;

        Ok(())
    }
}

Review comment (suggestion):
Here you can simply use `ctx.spawn_exit` to spawn a task and then set abort after the specified time. First create a new abort signal and convert it to a Value: `AbortSignal{...}.into_js()`. Then clone it and return one copy while pass the other copy to the async task which then sets the abort value after said time. Something like:
```rust
 pub fn timeout<'js>(ctx: Ctx<'js>, mills: size) -> Result<Class<'js,Self>> {
        let signal = AbortSignal {
            aborted: true,
            reason: reason.0,
        }
        
        let signal_instance =  Class::instance(ctx,signal);
        let signal_instance2 = signal_instance.clone()
        
        ctx.spawn_exit(async move {
           tokio::time::sleep(mills).await;
           let borrow = signal_instance.borrow();
           borrow.aborted = true;
           borrow.reason = "Timeout" //check spec
        })?;
        Ok(signal_instance2);
    }


Code after:
    #[qjs(get)]
    pub fn aborted(&self) -> bool {
        self.aborted
    }

    #[qjs(get)]
    pub fn reason(&self) -> Option<Value<'js>> {
        self.reason.clone()
    }

    #[qjs(skip)]
    pub fn set_reason(&mut self, reason: Opt<Value<'js>>) -> Result<Option<Value<'js>>> {
        if let Some(new_reason) = reason.0 {
            Ok(self.reason.replace(new_reason))
        } else {
            let old_reason = self.reason.take();
            Ok(old_reason)
        }
    }

    #[qjs(skip)]
    pub fn set_aborted(&mut self, is_aborted: bool) {
        self.aborted = is_aborted;
    }

    #[qjs(static)]
    pub fn abort(reason: Opt<Value<'js>>) -> AbortSignal {
        AbortSignal {
            aborted: true,
            reason: reason.0,
        }
    }

    #[qjs(static)]
    pub fn timeout(ctx: Ctx<'js>, milliseconds: u64) -> Result<Class<'js, Self>> {
        let timeout_exception = Exception::from_value("TimeoutError".into_js(&ctx)?)?; // TODO: Timeout DOMException
        let signal = AbortSignal {
            aborted: false,
            reason: None,
        };

        let signal_instance = Class::instance(ctx.clone(), signal)?;
        let signal_instance2 = signal_instance.clone();

        ctx.spawn_exit(async move {
            tokio::time::sleep(Duration::from_millis(milliseconds)).await;
            let mut borrow = signal_instance.borrow_mut();
            borrow.set_aborted(true);
            borrow.set_reason(Opt(Some(timeout_exception.into_value())))?;
            Ok(())
        })?;