我无法理解可以在onupgradeneeded事件中放置多少代码,以及如何确保该代码中对数据库的所有单独异步更改将始终在事务发生之前完成。
如果我正确理解了规范,那么模式交易" versionchange"在打开数据库的请求中触发onupgradeneeded事件时自动创建。
因此,在onupgradeneeded事件中编写的所有代码都被视为单个事务;并且我假设如果它到达oncomplete它会触发打开请求的onsuccess事件,如果它到达错误,则它会触发打开请求的onerror事件。
我对代码的复杂程度感到困惑。
例如,在事务中有另一个异步事件是否安全,例如" objectStore.transaction.oncomplete = function(event){}"在尝试写入对象存储之前等待创建对象存储?
或者是否应该在开放请求的onsuccess事件中执行在onupgradeneeded事务中创建的对象存储库中的数据写入,以确保已经创建了它?
而且,正如在MDN Web文档中的旧示例中所发现的那样,onupgradeneeded事件中是否应该有更多的db.onerror事件? https://developer.mozilla.org/en-US/docs/Web/API/IDBDatabase#setVersion()_.0A.0ADeprecated
另一个例子是我的数据库特有的。该数据库有一组投资组合,其中每个投资组合都有一个对象存储,如port_data_3和可变数量的模块存储,如module_3.2,表示第三个投资组合的第二个模块。此外,还有一个port_key对象存储,每个项目组合包含一条记录,其中包含项目组合名称及其唯一键,在此示例中为3。
如果用户决定从数据库中删除投资组合3,则需要更改版本,其中包括三个主要步骤。 1)必须删除port_key存储区中密钥3的单行; 2)必须删除port_data_3商店; 3)名称以' module_3开头的所有商店。'必须删除。
d = db.objectStoreNames,l = d.length之类的东西,可用于遍历d.item(i)以确定应删除哪些商店。但是,在所有这些个别删除完成或失败之前,交易是否始终保持开放?
类似地,投资组合的添加/删除以及投资组合中模块的添加/删除需要版本更改。拥有一个打开的函数是否安全,以便所有这些版本更改类型的代码都在一个更复杂的onupgradeneeded事件中,或者为每个版本的更改类型设置单独的打开函数是否更好,以便onupgradeneeded事件代码每个人都尽可能简单吗?
我想我的一些困惑是由于事务由多个独立的异步进程组成,而我无法控制它们如何被分组到一个事务事件中。我必须使用promises或Promise.all或异步函数或生成器在其余代码中执行类似的操作。它让我的小脑袋充满信心,让我确信我不会错过任何一种迟到的错误。
我认为如果开放请求包含在一个承诺中并且承诺放在一个带有等待的try / catch中,我觉得它会更安全;但是仍然没有改变onupgradeneeded及其版本更改事务如何确定所有这些异步进程是否成功或至少有一次失败。
感谢您提供的任何指示和解释。
回应Josh的回答
感谢您的详细解答。这对我有很大帮助。
这也有助于我理解你在5月回答我的一个问题的答案 - 也许是我在这里提交的第一个问题 - 我当时并没有真正理解后一部分。问题是How to map mulit-level object to indexedDB for best efficiency。
我想我现在可以根据你对这个问题的回答得到它。我可以更改数据库结构,这样我就不需要升级数据库,因为用户需要添加组合和模块。并且,正如您在回答上一个问题时所建议的那样,所需的三个关键字 - 组合,模块和模块下的项目 - 可以是三个索引,三者的组合也可以是索引,因为这将是查询最常需要的。事实上,三者的组合是我唯一的唯一标识符,除非我让浏览器生成一个。因此,代替可变数量的多个模块对象存储,每个需要数据库升级的添加,将存在相当于一个模块对象存储,其包含跨所有组合和所有模块的先前数据对象。同样可以将多个投资组合级别的商店合二为一。
这简化了该数据库,并且无需我所关注的onupgradeneeded代码/事务的最大部分。有了这些变化,我想我终于可以回到更新程序以使用indexedDB而不是localStorage,甚至可能再次开始睡觉。在这种情况下,codiing本身不是具有挑战性的部分,而是确定有效的数据库结构。
还要感谢有关如何更好地布置未来问题的信息。
答案 0 :(得分:1)
fetch
调用之类的操作并不安全。在运行时,事务将在调用解析之前完成,并且所有排队操作都将失败。fetch
之类的异步调用排队,然后等待提取结算,然后对该打开的事务执行插入操作,将无效,因为事务将完成在此之前,因为该提取无法在事件循环的下一个纪元开始时最早解决,但事务因为没有检测到新请求而提前解决。请注意您在Mozilla Developer Network(MDN)等地方找到的功能文档的一些细致措辞。例如store.delete(thing)
。 delete
功能在与商店相关联的交易中创建新请求。这是一个非常安全的异步事情。您可以创建任意数量的此类附加请求。您不必等待交易完成添加新请求。您不必等待其他请求完成以启动新请求。在开始事务之前,您不必等待事务完成(有一点需要注意,onupgradeneeded中的特殊版本更改事务)。
交易只是一组请求。这是一个分组,可以帮助您说请求作为一个群体一起生活和死亡。如果任何一个请求失败,整个组都会失败。这是交易的重点。它是如此有用,indexedDB为您提供了其他方式来发出请求,即使只有一个请求,您也被迫使用事务。如果您使用过SQL数据库,可能会遇到类似START TRANSACTION; SELECT ...; END TRANSACTION;
的语法。这里也是一样的。除了indexedDB不允许您在事务之外执行SELECT ...
,您可以在SQL中执行此操作。此外,indexedDB不允许您自己明确地结束事务。只需决定不创建更多请求并允许其在不久之后超时,即可完成indexedDB事务。
关于您的整体编程设计,我强烈建议您选择一种设计,当您的数据发生变化以及您的应用程序/世界中发生的事情时,您无需更改数据库架构。通常,架构应该是不变的。如果您发现自己需要经常创建和删除对象存储作为应用程序的正常操作,我会认真重新考虑设计。
此外,虽然您可以从onupgradeneeded中插入数据,但您通常应该在IDBOpenRequest的成功事件处理程序中执行此操作。传统规则(不是正式的规则)对数据的更改都会在onccess上进行,并且对模式的更改需要在onupgrade上进行。如果您在网络上遇到数据发生变化的示例,我建议您仔细阅读,这些示例通常只是在这些示例中快速完成并且不方便,而不是正确的应用程序设计。
顺便说一句,如果你将这个非常广泛的问题分解成更小的部分来突出你的混乱区域,并显示出意外工作的示例代码,或者在其中添加注释突出显示你没有的部分,你会得到更好的答案知道如何制定。