Javascript 我怎么知道IDBCursor到达了它';最后的价值是多少?
我试着像这样承诺:Javascript 我怎么知道IDBCursor到达了它';最后的价值是多少?,javascript,asynchronous,promise,indexeddb,Javascript,Asynchronous,Promise,Indexeddb,我试着像这样承诺: /** * @description Allows asynchronous looping over IDBCursor. The cursor request must be provided and must be new and unused! */ class IndexedDBAsyncCursor { /** * * @param {IndexedDBAsyncTable} parentTable * @param {
/**
* @description Allows asynchronous looping over IDBCursor. The cursor request must be provided and must be new and unused!
*/
class IndexedDBAsyncCursor {
/**
*
* @param {IndexedDBAsyncTable} parentTable
* @param {IDBRequest} cursorRequest
*/
constructor(parentTable, cursorRequest) {
this.cursorRequest = cursorRequest;
this.table = parentTable;
/** @type {Promise<IDBCursor>} **/
this.nextValuePromise = null;
/** @type {IDBCursor} **/
this.lastCursor = null;
this.hasNext = true;
this.hookCursorRequest();
}
/**
* @description Starts waiting for the next value
* @private
*/
makeNextValuePromise() {
if (this.nextValuePromise == null) {
this.rejectPromise = null;
this.resolvePromise =null;
this.nextValuePromise = new Promise((resolve, reject) => {
this.rejectPromise = reject;
this.resolvePromise = resolve;
});
}
}
/**
* Adds event listeners on the cursor
* @private
*/
hookCursorRequest() {
this.makeNextValuePromise();
this.cursorRequest.onsuccess = (event) => {
/** @type {IDBCursor} **/
const cursor = event.target.result;
this.lastCursor = cursor;
if (cursor) {
console.log("[IDB CURSOR] Next value: ", cursor);
this.resolvePromise(cursor);
}
else {
this.hasNext = false;
this.resolvePromise(null);
console.log("[IDB CURSOR] End.");
}
};
this.cursorRequest.onerror = (event) => {
this.hasNext = false;
this.rejectPromise(event);
}
}
/**
* @description Resolves with null or an IDBCursor
* @returns {Promise<IDBCursor>}
*/
async next() {
if (!this.hasNext)
return null;
if (this.lastCursor != null) {
this.makeNextValuePromise();
this.lastCursor.continue();
}
const result = await this.nextValuePromise;
this.nextValuePromise = null;
return result;
}
}
问题在于此代码:
else {
this.hasNext = false;
this.resolvePromise(null);
console.log("[IDB CURSOR] End.");
}
问题是,一旦达到最后一个值,onsuccess
就不会再次调用。它不再被调用,而我假设它将被最后一次调用,使用null
而不是IDBCursor
。但没有这样的事情发生
如何正确地做到这一点?对此答案并不完全有信心,但我认为您无法按游标请求完成承诺。您可以在整个光标移动过程中做出承诺,但不是每次迭代 原因有点复杂,但它与微任务有关,也与事务在没有检测到调用
游标的下一个请求时如何超时有关。请及时继续
以下是报价:
有问题的索引数据库事务与承诺的组合很差
事务被定义为具有活动标志,该标志在
事务被创建,并且当来自源的IDB事件回调
运行与该事务关联的。活动标志被清除
当任务完成时,即当控制从脚本返回时;对于
例如,在回调的末尾。事务中的操作
(put、get等)仅在标志为真时才允许。这意味着
您不能在承诺回调中执行操作,因为
根据定义,不是IDB事件回调。此外,交易
当清除标志且存在以下情况时自动尝试提交
没有未决的请求。这意味着即使之前的
取消限制后,事务将在任何
我被解雇了。如果要启用活动标志机制
如果完全删除,则需要引入新的提交模型
来源:对这个答案并不完全有信心,但我不认为你能做到每一个游标请求的承诺。您可以在整个光标移动过程中做出承诺,但不是每次迭代
原因有点复杂,但它与微任务有关,也与事务在没有检测到调用游标的下一个请求时如何超时有关。请及时继续
以下是报价:
有问题的索引数据库事务与承诺的组合很差
事务被定义为具有活动标志,该标志在
事务被创建,并且当来自源的IDB事件回调
运行与该事务关联的。活动标志被清除
当任务完成时,即当控制从脚本返回时;对于
例如,在回调的末尾。事务中的操作
(put、get等)仅在标志为真时才允许。这意味着
您不能在承诺回调中执行操作,因为
根据定义,不是IDB事件回调。此外,交易
当清除标志且存在以下情况时自动尝试提交
没有未决的请求。这意味着即使之前的
取消限制后,事务将在任何
我被解雇了。如果要启用活动标志机制
如果完全删除,则需要引入新的提交模型
来源:所展示的代码在Chrome中适用,但在Firefox中不适用。仅供参考,以下是我曾经驾驶过的:
indexedDB.deleteDatabase('so');
const open = indexedDB.open('so');
open.onupgradeneeded = e => {
const db = open.result;
const s = db.createObjectStore('s');
for (let i = 0; i < 4; ++i) {
s.put({name: 's' + i, num: i}, i);
}
};
open.onsuccess = async e => {
const db = open.result;
const tx = db.transaction('s');
const objectStore = tx.objectStore('s')
const values = [], oneOnly = false;
const predicate = x => true;
const cursor = new IndexedDBAsyncCursor(this,objectStore.openCursor());
/** @type {IDBCursor} **/
var value = null;
while (value = await cursor.next()) {
if (predicate(value)) {
values.push(value.value);
console.log("[IDB] Found value: ",value.value)
if (oneOnly)
break;
}
else {
console.log("[IDB] Value does not match predicate: ",value.value)
}
}
};
问题在于,Chrome和Firefox在执行微任务(即承诺的“then”回调)时不一致。Chrome符合规范,微任务在整个任务中执行。Firefox尚未修复,微任务稍后执行。详情请浏览
正如其他Josh所暗示的,这是一个问题,因为在Firefox中,continue()
调用最终发生在“success
”事件处理程序之外,这是不允许的。您可以通过对async next()
实现进行以下代码更改来查看:
try {
this.lastCursor.continue();
} catch (ex) { console.error(ex.name, ex.message); }
在Firefox中,此日志记录:“TransactionActiveError针对当前未激活或已完成的事务发出了请求。”——因为Firefox执行包含事件任务外的下一个调用的微任务,所以该事务不再处于活动状态
为了在Firefox修复微任务问题之前实现这一点,需要重新构造代码,直接在“success
”处理程序中调用continue()
,而不管是否使用下一个值,这将需要重新设计跟踪/生成承诺的方式
请注意,即使在Firefox中修复了微任务问题,所编写的代码仍然很脆弱,因为在循环中执行其他任何异步操作(即引入另一个wait
)可能会将continue()
调用从任务中推出。因此,如果您想(例如)在迭代时执行fetch()
,它将中断。如上所述,直接在“success
”处理程序中执行continue()
,将使其更加健壮。所示的代码在Chrome中适用,但在Firefox中不适用。仅供参考,以下是我曾经驾驶过的:
indexedDB.deleteDatabase('so');
const open = indexedDB.open('so');
open.onupgradeneeded = e => {
const db = open.result;
const s = db.createObjectStore('s');
for (let i = 0; i < 4; ++i) {
s.put({name: 's' + i, num: i}, i);
}
};
open.onsuccess = async e => {
const db = open.result;
const tx = db.transaction('s');
const objectStore = tx.objectStore('s')
const values = [], oneOnly = false;
const predicate = x => true;
const cursor = new IndexedDBAsyncCursor(this,objectStore.openCursor());
/** @type {IDBCursor} **/
var value = null;
while (value = await cursor.next()) {
if (predicate(value)) {
values.push(value.value);
console.log("[IDB] Found value: ",value.value)
if (oneOnly)
break;
}
else {
console.log("[IDB] Value does not match predicate: ",value.value)
}
}
};
问题在于,Chrome和Firefox在执行微任务(即承诺的“then”回调)时不一致。Chrome符合规范,微任务在整个任务中执行。Firefox尚未修复,微任务稍后执行。详情请浏览
正如其他Josh所暗示的,这是一个问题,因为在Firefox中,continue()
调用最终发生在“success
”事件处理程序之外,这是不允许的。您可以通过对async next()
实现进行以下代码更改来查看:
try {
this.lastCursor.continue();
} catch (ex) { console.error(ex.name, ex.message); }
在Firefox中记录:“TransactionActiveError针对当前未激活或已完成的事务发出请求。”——因为Firefox执行包含以下内容的微任务: