尝试打开具有更高版本号的数据库时触发 onblocked
onblocked fired when trying to open db with higher version number
我正在创建 indexedDB 数据库 products_db
。
onload
将 objectStore products_os
添加到数据库。
- 正在从服务器获取数据并将数据添加到
products_os
。
- 显示 UI 中的数据。
onclick
的 添加 按钮,尝试打开具有更高版本号的数据库并创建新的 objectStore cart_os
。 这是它没有按预期工作的地方。
当前行为:
- 当我以隐身模式打开 chrome 浏览器并在选项卡中加载我的 index.html 文件时,table 行显示在UI,但在
Developer Tools
的 Application
选项卡中 indexedDB
中没有 product_db
数据库。
- 单击添加按钮时,
onblocked
事件的警报如
function addCartData(e) {
弹出。 (此站点打开时没有其他选项卡!)。关闭弹出窗口后,现在单击相同的添加按钮将不会执行任何操作。
- 现在,如果我使用 cmd + shift + r 刷新页面,新行将附加到 table。现在共有两行。每次刷新时,都会附加一个新行。前一点对于 UI 中的所有添加按钮都是正确的,点击任何一个都会触发警报,然后所有按钮都变得无用。(没有弹出 onclick)。
- 如果我清除存储然后执行 cmd + shift + r,我再次在 UI.
中只得到一行
预期行为:
- Application 选项卡中 Developer tools 的 indexedDB 不应为空。相反,它应该填充
db
和 objectStore
.
- 刷新时,不清除存储,应该只有一行
每次都显示。
添加按钮的 onclick
,应该打开一个带有新版本号的数据库,并且应该向其中添加 objectStore cart_os
。以便稍后我可以使用 cart_os
进行 read/write
交易。
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Product Table</title>
<link href="styles.css" rel="stylesheet">
<script src="script.js" defer></script>
</head>
<body>
<table id='idb-table' style="width=100%" />
</body>
</html>
script.js
// Create an instance of a db object for us to store the open database in
let db;
window.onload = function() {
// Open our database; it is created if it doesn't already exist
let request = window.indexedDB.open('products_db', 1);
// onerror handler signifies that the database didn't open successfully
request.onerror = function() {
console.log('Database failed to open');
}
// onsuccess handler signifies that the database opened successfully
request.onsuccess = function() {
console.log('Database opened successfully');
// Store the opened database object in the db variable. This is used a lot below
db = request.result;
// Run the fetchProducts() function to fetch data from external API
fetchProducts();
}
// Setup the database tables if this has not already been done
request.onupgradeneeded = function(e) {
// Grab a reference to the opened database
let db = e.target.result;
// Create an objectStore to store our products in (basically like a single table)
// including a auto-incrementing key
let objectStore = db.createObjectStore('products_os', {keyPath: 'id', autoIncrement: true});
// Define what data items the objectStore will contain
objectStore.createIndex('title', 'title', {unique: false});
objectStore.createIndex('price', 'price', {unique: false});
objectStore.createIndex('inStock', 'inStock', {unique: false});
}
function fetchProducts() {
let product1 = fetch('http://localhost:3001/location/pathname') // see product.json file
.then(function(response) {
return response.json();
})
.then(function(response) {
addData(response);
})
}
function addData(value) {
// open a read/write db transaction, ready for adding the data
let transaction = db.transaction(['products_os'], 'readwrite');
// call an object store that's already been added to the database
let objectStore = transaction.objectStore('products_os');
let newItem = {
title: value.title,
price: value.sellingPrice,
inStock: value.inStock,
};
// Make a request to add our newItem object to the object store
var request = objectStore.add(newItem);
transaction.oncomplete = function() {
console.log('Transaction completed: database modification finished.');
// update the display of data to show the newly added item, by running displayData() again.
displayData();
}
transaction.onerror = function() {
console.log('Transaction not opened due to error');
};
}
function displayData() {
// Open our object store and then get a cursor - which iterates through all the
// different data items in the store
let objectStore = db.transaction('products_os').objectStore('products_os');
objectStore.openCursor().onsuccess = function(e) {
// Get a reference to the cursor
let cursor = e.target.result;
// Get a reference to table
let table = document.querySelector('#idb-table');
// If there is still another data item to iterate through, keep running this code
if(cursor) {
// Insert into table,
// structure the HTML fragment, and append it inside the table
let tableRow = document.createElement('tr');
for (let val in cursor.value) {
if (Object.prototype.hasOwnProperty.call(cursor.value, val)) {
if (val !== 'id') {
let td = document.createElement('td');
tableRow.appendChild(td);
td.textContent = cursor.value[val];
}
}
}
let td = document.createElement('td');
td.setAttribute('align', 'center');
let addButton = document.createElement('button');
td.appendChild(addButton);
addButton.textContent = 'Add';
addButton.addEventListener('click', function(e) {
addCartData(e);
})
tableRow.appendChild(td);
table.appendChild(tableRow);
// Iterate to the next item in the cursor
cursor.continue();
}
}
}
function addCartData(e) {
// Open the db with higher version number
let req = window.indexedDB.open('products_db', 2);
req.onerror = function() {
console.log('Database failed to open');
}
req.onsuccess = function() {
console.log('Database opened successfully');
// Store the opened database object in the db variable. This is used a lot below
db = req.result;
}
req.onblocked = function(event) {
// If some other tab is loaded with the database, then it needs to be closed
// before we can proceed.
alert("Please close all other tabs with this site open!");
};
req.onupgradeneeded = function(e){
// Grab a reference to the opened database
let db = e.target.result;
// Create a cart objectStore to store our information added to the cart
let cartStore = db.createObjectStore('cart_os', {keyPath: 'id', autoIncrement: true});
// Define what data items the cart objectStore will contain
cartStore.createIndex('title', 'title', {unique: false});
cartStore.createIndex('price', 'price', {unique: false});
}
}
styles.css
table {
font-family: arial, sans-serif;
border-collapse: collapse;
width: 100%;
}
td, th {
border: 1px solid #dddddd;
text-align: left;
padding: 8px;
}
tr:nth-child(even) {
background-color: #dddddd;
}
tr:last-child {
text-align: center!important;
}
product.json
{
"title": "Newhide Designer",
"productDescription": "",
"sellingPrice": 119,
"inStock": true,
}
编辑
objectStore products_os
was never added to db
我在第 207 行收到错误 Uncaught (in promise) DOMException: Failed to execute 'transaction' on 'IDBDatabase': One of the specified object stores was not found.
,如图所示。
onupgradeneeded_version1:
function onupgradeneeded_version1(e) {
// Grab a reference to the opened database
let db = e.target.result;
// Create an objectStore to store our products in (basically like a single table)
// including a auto-incrementing key
let objectStore = db.createObjectStore('products_os', {keyPath: 'id', autoIncrement: true});
// Define what data items the objectStore will contain
objectStore.createIndex('title', 'title', {unique: false});
objectStore.createIndex('price', 'price', {unique: false});
objectStore.createIndex('inStock', 'inStock', {unique: false});
}
嗯,关于如何改进这段代码,我有很多建议,但为了直截了当,我认为问题源于您最初打开数据库连接并保持打开状态(无限期),然后在您的点击处理程序中,打开具有更高版本号的第二个数据库版本,如果单击该版本,则在初始数据库连接仍处于打开状态时发生。
我建议将 indexedDB.open => db
逻辑封装在一个 promise 中。这将使代码更易于阅读和编写。然后,我建议更改您使用该承诺的方式。
这是我的想法:
function open(name, version, onupgradeneeded) {
return new Promise(function executor(resolve, reject) {
const request = indexeDB.open(name, version);
request.onsuccess = function(event) { resolve(request.result); };
request.onerror = function(event) { reject(request.result); };
request.onblocked = function(event) { console.debug('blocked indefinitely'); };
request.onupgradeneeded = onupgradeneeded;
});
}
async function fetch_product() {
const response = await fetch('http://localhost:3001/location/pathname');
const data = await response.json();
return data;
}
function add_product(db, data) {
return new Promise(function(resolve, reject) {
const transaction = db.transaction(..., 'readwrite');
transaction.oncomplete = resolve;
transaction.onerror = reject;
const store = transaction.objectStore(...);
store.put(data);
});
}
function get_products(db) {
return new Promise(function(resolve, reject) {
const transaction = db.transaction(...);
const store = transaction.objectStore(...);
const request = store.getAll();
request.onsuccess = function(event) {
const products_array = request.result;
resolve(products_array);
};
request.onerror = function(event) {
reject(request.error);
};
});
}
function render_product(product) {
const table = document.getElementById('table-id');
// create rows and all that.
// and append
button.onclick = handle_click;
}
async function handle_click(event) {
// Based on what was clicked, find and build the product data object
// event.target is the button
const row = event.target.closest('tr');
const name = row.querySelector('td.namecolumn').textContent;
// etc
const product = {
name: name
};
const db = await open(name, version2, onupgradeneeded_for_version2);
await add_product(db, product);
db.close();
}
async function onload() {
const product = await fetch_product();
const db = await open(name, version, onupgradeneeded_version1);
await add_product(db, product);
const products = await get_products(db);
// we are done with the db now
db.close();
// print out product data
for(const product of products) {
render_product(product);
}
}
我正在创建 indexedDB 数据库 products_db
。
onload
将 objectStoreproducts_os
添加到数据库。- 正在从服务器获取数据并将数据添加到
products_os
。 - 显示 UI 中的数据。
onclick
的 添加 按钮,尝试打开具有更高版本号的数据库并创建新的 objectStorecart_os
。 这是它没有按预期工作的地方。
当前行为:
- 当我以隐身模式打开 chrome 浏览器并在选项卡中加载我的 index.html 文件时,table 行显示在UI,但在
Developer Tools
的Application
选项卡中indexedDB
中没有product_db
数据库。 - 单击添加按钮时,
onblocked
事件的警报如function addCartData(e) {
弹出。 (此站点打开时没有其他选项卡!)。关闭弹出窗口后,现在单击相同的添加按钮将不会执行任何操作。 - 现在,如果我使用 cmd + shift + r 刷新页面,新行将附加到 table。现在共有两行。每次刷新时,都会附加一个新行。前一点对于 UI 中的所有添加按钮都是正确的,点击任何一个都会触发警报,然后所有按钮都变得无用。(没有弹出 onclick)。
- 如果我清除存储然后执行 cmd + shift + r,我再次在 UI. 中只得到一行
预期行为:
- Application 选项卡中 Developer tools 的 indexedDB 不应为空。相反,它应该填充
db
和objectStore
. - 刷新时,不清除存储,应该只有一行 每次都显示。 添加按钮的
onclick
,应该打开一个带有新版本号的数据库,并且应该向其中添加 objectStorecart_os
。以便稍后我可以使用cart_os
进行read/write
交易。
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Product Table</title>
<link href="styles.css" rel="stylesheet">
<script src="script.js" defer></script>
</head>
<body>
<table id='idb-table' style="width=100%" />
</body>
</html>
script.js
// Create an instance of a db object for us to store the open database in
let db;
window.onload = function() {
// Open our database; it is created if it doesn't already exist
let request = window.indexedDB.open('products_db', 1);
// onerror handler signifies that the database didn't open successfully
request.onerror = function() {
console.log('Database failed to open');
}
// onsuccess handler signifies that the database opened successfully
request.onsuccess = function() {
console.log('Database opened successfully');
// Store the opened database object in the db variable. This is used a lot below
db = request.result;
// Run the fetchProducts() function to fetch data from external API
fetchProducts();
}
// Setup the database tables if this has not already been done
request.onupgradeneeded = function(e) {
// Grab a reference to the opened database
let db = e.target.result;
// Create an objectStore to store our products in (basically like a single table)
// including a auto-incrementing key
let objectStore = db.createObjectStore('products_os', {keyPath: 'id', autoIncrement: true});
// Define what data items the objectStore will contain
objectStore.createIndex('title', 'title', {unique: false});
objectStore.createIndex('price', 'price', {unique: false});
objectStore.createIndex('inStock', 'inStock', {unique: false});
}
function fetchProducts() {
let product1 = fetch('http://localhost:3001/location/pathname') // see product.json file
.then(function(response) {
return response.json();
})
.then(function(response) {
addData(response);
})
}
function addData(value) {
// open a read/write db transaction, ready for adding the data
let transaction = db.transaction(['products_os'], 'readwrite');
// call an object store that's already been added to the database
let objectStore = transaction.objectStore('products_os');
let newItem = {
title: value.title,
price: value.sellingPrice,
inStock: value.inStock,
};
// Make a request to add our newItem object to the object store
var request = objectStore.add(newItem);
transaction.oncomplete = function() {
console.log('Transaction completed: database modification finished.');
// update the display of data to show the newly added item, by running displayData() again.
displayData();
}
transaction.onerror = function() {
console.log('Transaction not opened due to error');
};
}
function displayData() {
// Open our object store and then get a cursor - which iterates through all the
// different data items in the store
let objectStore = db.transaction('products_os').objectStore('products_os');
objectStore.openCursor().onsuccess = function(e) {
// Get a reference to the cursor
let cursor = e.target.result;
// Get a reference to table
let table = document.querySelector('#idb-table');
// If there is still another data item to iterate through, keep running this code
if(cursor) {
// Insert into table,
// structure the HTML fragment, and append it inside the table
let tableRow = document.createElement('tr');
for (let val in cursor.value) {
if (Object.prototype.hasOwnProperty.call(cursor.value, val)) {
if (val !== 'id') {
let td = document.createElement('td');
tableRow.appendChild(td);
td.textContent = cursor.value[val];
}
}
}
let td = document.createElement('td');
td.setAttribute('align', 'center');
let addButton = document.createElement('button');
td.appendChild(addButton);
addButton.textContent = 'Add';
addButton.addEventListener('click', function(e) {
addCartData(e);
})
tableRow.appendChild(td);
table.appendChild(tableRow);
// Iterate to the next item in the cursor
cursor.continue();
}
}
}
function addCartData(e) {
// Open the db with higher version number
let req = window.indexedDB.open('products_db', 2);
req.onerror = function() {
console.log('Database failed to open');
}
req.onsuccess = function() {
console.log('Database opened successfully');
// Store the opened database object in the db variable. This is used a lot below
db = req.result;
}
req.onblocked = function(event) {
// If some other tab is loaded with the database, then it needs to be closed
// before we can proceed.
alert("Please close all other tabs with this site open!");
};
req.onupgradeneeded = function(e){
// Grab a reference to the opened database
let db = e.target.result;
// Create a cart objectStore to store our information added to the cart
let cartStore = db.createObjectStore('cart_os', {keyPath: 'id', autoIncrement: true});
// Define what data items the cart objectStore will contain
cartStore.createIndex('title', 'title', {unique: false});
cartStore.createIndex('price', 'price', {unique: false});
}
}
styles.css
table {
font-family: arial, sans-serif;
border-collapse: collapse;
width: 100%;
}
td, th {
border: 1px solid #dddddd;
text-align: left;
padding: 8px;
}
tr:nth-child(even) {
background-color: #dddddd;
}
tr:last-child {
text-align: center!important;
}
product.json
{
"title": "Newhide Designer",
"productDescription": "",
"sellingPrice": 119,
"inStock": true,
}
编辑
objectStore products_os
was never added to db
我在第 207 行收到错误 Uncaught (in promise) DOMException: Failed to execute 'transaction' on 'IDBDatabase': One of the specified object stores was not found.
,如图所示。
onupgradeneeded_version1:
function onupgradeneeded_version1(e) {
// Grab a reference to the opened database
let db = e.target.result;
// Create an objectStore to store our products in (basically like a single table)
// including a auto-incrementing key
let objectStore = db.createObjectStore('products_os', {keyPath: 'id', autoIncrement: true});
// Define what data items the objectStore will contain
objectStore.createIndex('title', 'title', {unique: false});
objectStore.createIndex('price', 'price', {unique: false});
objectStore.createIndex('inStock', 'inStock', {unique: false});
}
嗯,关于如何改进这段代码,我有很多建议,但为了直截了当,我认为问题源于您最初打开数据库连接并保持打开状态(无限期),然后在您的点击处理程序中,打开具有更高版本号的第二个数据库版本,如果单击该版本,则在初始数据库连接仍处于打开状态时发生。
我建议将 indexedDB.open => db
逻辑封装在一个 promise 中。这将使代码更易于阅读和编写。然后,我建议更改您使用该承诺的方式。
这是我的想法:
function open(name, version, onupgradeneeded) {
return new Promise(function executor(resolve, reject) {
const request = indexeDB.open(name, version);
request.onsuccess = function(event) { resolve(request.result); };
request.onerror = function(event) { reject(request.result); };
request.onblocked = function(event) { console.debug('blocked indefinitely'); };
request.onupgradeneeded = onupgradeneeded;
});
}
async function fetch_product() {
const response = await fetch('http://localhost:3001/location/pathname');
const data = await response.json();
return data;
}
function add_product(db, data) {
return new Promise(function(resolve, reject) {
const transaction = db.transaction(..., 'readwrite');
transaction.oncomplete = resolve;
transaction.onerror = reject;
const store = transaction.objectStore(...);
store.put(data);
});
}
function get_products(db) {
return new Promise(function(resolve, reject) {
const transaction = db.transaction(...);
const store = transaction.objectStore(...);
const request = store.getAll();
request.onsuccess = function(event) {
const products_array = request.result;
resolve(products_array);
};
request.onerror = function(event) {
reject(request.error);
};
});
}
function render_product(product) {
const table = document.getElementById('table-id');
// create rows and all that.
// and append
button.onclick = handle_click;
}
async function handle_click(event) {
// Based on what was clicked, find and build the product data object
// event.target is the button
const row = event.target.closest('tr');
const name = row.querySelector('td.namecolumn').textContent;
// etc
const product = {
name: name
};
const db = await open(name, version2, onupgradeneeded_for_version2);
await add_product(db, product);
db.close();
}
async function onload() {
const product = await fetch_product();
const db = await open(name, version, onupgradeneeded_version1);
await add_product(db, product);
const products = await get_products(db);
// we are done with the db now
db.close();
// print out product data
for(const product of products) {
render_product(product);
}
}