我应该每次打开一个IDBDatabase还是保持一个实例打开?

时间:2016-11-14 16:18:59

标签: javascript indexeddb

我有一个SPA应用程序,它将对IndexedDB进行多次读/写。

打开数据库是一个带回调的异步操作:

var db; 
var request = window.indexedDB.open("MyDB", 2);

request.onupgradeneeded = function(event) { 
    // Upgrade to latest version...
}

request.onerror = function(event) { 
    // Uh oh...
}

request.onsuccess = function(event) {
    // DB open, now do something
    db = event.target.result;
};

我可以通过两种方式使用此db实例:

  1. 在页面/ SPA的生命周期内保留一个db个实例?
  2. 当前操作完成后调用db.close()并在下一次操作中打开一个新操作?
  3. 这两种模式都存在缺陷吗?保持indexedDB开放是否有任何风险/问题?每个open操作是否有开销/延迟(超过可能的升级)?

2 个答案:

答案 0 :(得分:4)

每次打开连接可能会因浏览器执行更多工作而变慢(例如,它可能需要从磁盘读取数据)。否则,没有真正的不利因素。

由于您提到了升级,因此任一模式都需要采用不同的方法来处理用户在另一个选项卡中打开页面的情况,并尝试使用更高版本打开数据库(因为它从服务器下载了更新的代码)。假设旧选项卡是版本3,新选项卡是版本4.

在每个操作一个连接的情况下,您会发现版本3上的open()失败,因为另一个选项卡能够升级到版本4.您可以注意到打开失败了{{1例如并告知用户他们需要刷新页面。

在每页一个连接的情况下,版本3的连接将阻止另一个选项卡。 v4选项卡可以响应请求中的VersionError事件,并让用户知道应关闭旧选项卡。或者v3选项卡可以响应连接上的"blocked"事件,并告诉用户需要关闭它。或两者兼而有之。

答案 1 :(得分:3)

我发现每次操作打开一个连接并不会严重降低性能。我已经运行了一年多的本地Chrome扩展程序,涉及大量的indexedDB操作,并且已经分析了数百次并且从未见过打开连接作为瓶颈。瓶颈来自于不正确使用索引或存储大块的事情。

基本上,不要在此基于性能做出决定。在连接方面,这确实不是问题。

问题实际上是你的代码的人体工程学,你对API的反应程度,以及你看到代码时代码的直观程度,你认为代码是多么敏感,对新鲜眼睛的热情程度如何(你自己一个月后,或其他人)。在处理阻塞问题时,这是非常值得注意的,这是间接处理应用程序模态。

我个人认为,如果您对编写异步Javascript感到满意,请使用您喜欢的任何方法。如果您在使用异步代码时遇到困难,选择始终打开连接将避免出现任何问题。我绝不会建议对一个比异步代码更新的人使用单个全局页面生命周期变量。您还将变量保留在页面的生命周期中。另一方面,如果你发现async琐碎,并且发现全局db变量更合适,那么一定要使用它。

编辑 - 根据您的评论,我想我会分享一些个人偏好的伪代码:

function connect(name, version) {
  return new Promise((resolve, reject) => {
    const request = indexedDB.open(name, version);
    request.onupgradeneeded = onupgradeneeded;
    request.onsuccess = () => resolve(request.result);
    request.onerror = () => reject(request.error);
    request.onblocked = () => console.warn('pending till unblocked');
  });
}

async foo(bar) {
  let conn;
  try {
    conn = await connect(DBNAME, DBVERSION);
    await storeBar(conn, bar);
  } finally {
    if(conn)
      conn.close();
  }
}

function storeBar(conn, bar) {
  return new Promise((resolve, reject) => {
    const tx = conn.transaction('store');
    const store = tx.objectStore('store');
    const request = store.put(bar);
    request.onsuccess = () => resolve(request.result);
    request.onerror = () => reject(request.error);
  });
}

使用async / await,在操作函数中添加额外的conn = await connect()行没有太大的摩擦。