如何使用 rusty_v8 处理异步操作

问题描述 投票:0回答:1

我正在尝试在 v8 (rusty_v8) 之上开发一个简单的 js 运行时,但我在异步方面遇到了一些麻烦。

我有一个函数,它是 js 领域的入口点:

fn message_from_worker(
    scope: &mut v8::HandleScope,
    args: v8::FunctionCallbackArguments,
    _ret: v8::ReturnValue,
)

// ...

此函数从 js 领域获取不同类型的消息,并用它们执行不同的操作。其中一条消息是执行长时间运行任务(获取)的请求。我希望任务完成后能够将消息发送回 js 领域。

到目前为止,这就是我所拥有的:

fn message_from_worker ... {

  // ...
    let message = args.get(0);
    let message = message.to_object(scope).unwrap();

    let kind = utils::get(scope, message, "kind").to_rust_string_lossy(scope);

    match kind.as_str() {
        // ...
        "fetch" => {
            let request = utils::get(scope, message, "request");
            let request = request.to_object(scope).unwrap();
  
            let url = utils::get(scope, request, "url").to_rust_string_lossy(scope);

            let callback = utils::get(scope, message, "sendResponse");
            let callback = match v8::Local::<v8::Function>::try_from(callback) {
                Ok(callback) => callback,
                Err(_) => {
                    utils::throw_type_error(scope, "sendResponse is not a function");
                    return;
                }
            };

            // We want to perform our async http request here

            let response = { /* for now response is a sync mock */ };

            let undefined = v8::undefined(scope).into();

            callback.call(scope, undefined, &[response.into()]);
        }

        // ...

天真的方法(使用阻塞请求)不起作用:

reqwest::blocking::get(&url).unwrap()

Cannot drop a runtime in a context where blocking is not allowed. This happens when a runtime is dropped from within an asynchronous context.


所以我正在考虑在全局状态下注册所有挂起的操作,然后从主线程轮询它们。但我不知道该怎么做。

我什至不知道是否可以同时轮询多个操作,处理其中一个操作的分辨率,然后继续轮询其余操作。

rust runtime v8 deno
1个回答
0
投票

要使用 V8 运行时处理 Rust 应用程序中的异步操作,您可以利用 Tokio 运行时以及

tokio::task
模块进行异步任务管理。总体思路是为长时间运行的操作生成异步任务并等待其完成,而不会阻塞 V8 运行时。

以下是使用

tokio
reqwest
包进行异步 HTTP 请求的示例:

use rusty_v8 as v8;
use std::sync::{Arc, Mutex};
use tokio::task;

// A structure to hold information about the async tasks
struct AsyncTask {
    callback: v8::Global<v8::Function>,
    url: String,
}

// A global state to store pending async tasks
lazy_static::lazy_static! {
    static ref PENDING_TASKS: Arc<Mutex<Vec<AsyncTask>>> = Arc::new(Mutex::new(Vec::new()));
}

// Function to register an async task
fn register_async_task(scope: &mut v8::HandleScope, task: AsyncTask) {
    PENDING_TASKS.lock().unwrap().push(task);
}

// Function to poll and complete async tasks
async fn poll_async_tasks() {
    loop {
        // Clone the list of tasks to avoid locking for too long
        let tasks = PENDING_TASKS.lock().unwrap().clone();
        for task in tasks {
            let url = task.url.clone();
            let callback = task.callback.clone();
            
            // Spawn an async task for each async operation
            tokio::spawn(async move {
                // Perform the async operation (e.g., HTTP request)
                let response = reqwest::get(&url).await.unwrap(); // Handle errors appropriately
                
                // Execute the callback in the V8 context with the response
                let scope = &mut v8::HandleScope::current();
                let undefined = v8::undefined(scope).into();
                callback.call(scope, undefined, &[response.text().unwrap().into()]);
            });
        }

        // Clear completed tasks from the global state
        PENDING_TASKS.lock().unwrap().clear();

        // Sleep for a while before checking again
        tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;
    }
}

// Function to initialize the asynchronous task polling loop
fn init_async_task_polling() {
    tokio::spawn(poll_async_tasks());
}

// Your existing function
fn message_from_worker(scope: &mut v8::HandleScope, args: v8::FunctionCallbackArguments, _ret: v8::ReturnValue) {
    // ...
    let url = /* extract URL from message */;
    let callback = /* extract callback from message */;

    // Register the async task
    register_async_task(scope, AsyncTask { callback, url: url.clone() });

    // Continue with other tasks or return
}

此示例使用

tokio::spawn
函数为每个异步操作生成异步任务。
poll_async_tasks
函数负责定期检查这些任务的状态。请注意,这只是一个基本示例,您可能需要根据您的具体要求和错误处理策略进行调整。

确保在应用程序启动时通过调用

init_async_task_polling
初始化异步任务轮询循环。此外,在异步任务中适当处理错误,并根据应用程序的需求调整轮询循环中的睡眠持续时间。

请记住将必要的依赖项添加到您的“Cargo.toml”中:

[dependencies]
tokio = { version = "1", features = ["full"] }
reqwest = "0.11"
rusty_v8 = "0.12"
lazy_static = "1.4"

这种方法允许您在不阻塞 V8 运行时的情况下执行异步操作,并支持并行执行多个异步任务。

© www.soinside.com 2019 - 2024. All rights reserved.