#web #wasm #async #web-apps #events #wasn

indexed-db

绑定到 IndexedDB,默认事务为中止并支持多线程操作

10 个不稳定版本 (3 个重大变更)

0.4.1 2024年2月25日
0.4.0 2024年1月29日
0.3.4 2024年1月20日
0.2.2 2024年1月14日
0.1.0 2024年1月14日

数据库接口 中排名第 278

Download history 11/week @ 2024-04-20 16/week @ 2024-04-27 14/week @ 2024-05-04 14/week @ 2024-05-11 9/week @ 2024-05-18 130/week @ 2024-05-25 48/week @ 2024-06-01 29/week @ 2024-06-08 39/week @ 2024-06-15 27/week @ 2024-06-22 26/week @ 2024-06-29 14/week @ 2024-07-06 7/week @ 2024-07-13 30/week @ 2024-07-20 61/week @ 2024-07-27 19/week @ 2024-08-03

每月下载量 119

MIT/Apache

88KB
1.5K SLoC

indexed-db

IndexedDB 的绑定,默认事务为中止并支持多线程操作。

为什么还需要另一个 IndexedDB crate?

在我编写此 crate 的时候,其他替代方案都有默认将 IndexedDB 事务提交的行为。这是因为在 IndexedDB 事务中,提交具有奇怪的语义:一旦应用程序在没有进行任何请求的情况下返回到事件循环,它们就会立即提交。

这个 crate 强制你的事务遵守 IndexedDB 的要求,这样就可以在出现错误时中止事务,而不是自动提交。

顺便提一下,在发布 0.4.0 版本时,这个 crate 是唯一在 wasm-bindgen 的多线程执行器下运行良好的 IndexedDB crate。你可以在 这个线程 中找到所有详细信息。

错误处理

此 crate 使用 Error<Err> 类型。基本上,所有由这个 crate 提供的结构体都有 Err 泛型参数。它是 indexed-db 使用周围的代码中用户的类型,以便于使用。

特别是,如果你想要恢复一个通过 indexed-db 代码传递的自定义错误(类型为 Err),你只需要将错误与 Error::User(_) 匹配,你将能够恢复你的自定义错误详细信息。

另一方面,当您的回调之一想要通过 indexed-db 返回您自己的类型的错误时,它可以使用 From<Err> for Error<Err> 实现。这是由 ? 运算符自动完成的,或者可以通过使用 return Err(e.into()); 进行显式返回。

示例

use anyhow::Context;
use indexed_db::Factory;
use web_sys::js_sys::JsString;

async fn example() -> anyhow::Result<()> {
    // Obtain the database builder
    // This database builder will let us easily use custom errors of type
    // `std::io::Error`.
    let factory = Factory::<std::io::Error>::get().context("opening IndexedDB")?;

    // Open the database, creating it if needed
    let db = factory
        .open("database", 1, |evt| async move {
            let db = evt.database();
            let store = db.build_object_store("store").auto_increment().create()?;

            // You can also add objects from this callback
            store.add(&JsString::from("foo")).await?;

            Ok(())
        })
        .await
        .context("creating the 'database' IndexedDB")?;

    // In a transaction, add two records
    db.transaction(&["store"])
        .rw()
        .run(|t| async move {
            let store = t.object_store("store")?;
            store.add(&JsString::from("bar")).await?;
            store.add(&JsString::from("baz")).await?;
            Ok(())
        })
        .await?;

    // In another transaction, read the first record
    db.transaction(&["store"])
        .run(|t| async move {
            let data = t.object_store("store")?.get_all(Some(1)).await?;
            if data.len() != 1 {
                Err(std::io::Error::new(
                    std::io::ErrorKind::Other,
                    "Unexpected data length",
                ))?;
            }
            Ok(())
        })
        .await?;

    // If we return `Err` (or panic) from a transaction, then it will abort
    db.transaction(&["store"])
        .rw()
        .run(|t| async move {
            let store = t.object_store("store")?;
            store.add(&JsString::from("quux")).await?;
            if store.count().await? > 3 {
                // Oops! In this example, we have 4 items by this point
                Err(std::io::Error::new(
                    std::io::ErrorKind::Other,
                    "Too many objects in store",
                ))?;
            }
            Ok(())
        })
        .await
        .unwrap_err();

    // And no write will have happened
    db.transaction(&["store"])
        .run(|t| async move {
            let num_items = t.object_store("store")?.count().await?;
            assert_eq!(num_items, 3);
            Ok(())
        })
        .await?;

    // More complex example: using cursors to iterate over a store
    db.transaction(&["store"])
        .run(|t| async move {
            let mut all_items = Vec::new();
            let mut cursor = t.object_store("store")?.cursor().open().await?;
            while let Some(value) = cursor.value() {
                all_items.push(value);
                cursor.advance(1).await?;
            }
            assert_eq!(all_items.len(), 3);
            assert_eq!(all_items[0], **JsString::from("foo"));
            Ok(())
        })
        .await?;

    Ok(())
}

依赖关系

~7–9.5MB
~185K SLoC