IndexedDB 仅在切换标签后未定义承诺退货

e1xvtsh3  于 2022-12-09  发布在  IndexedDB
关注(0)|答案(2)|浏览(147)

我正在做一个chrome扩展,它允许用户在YouTube视频上做笔记。笔记是使用IndexedDB存储的。我遇到了一个问题,如果我切换到另一个标签页,然后又切换回来,一个承诺返回undefined。首先,我使用的大部分代码使这个问题更容易理解。

// background.js

chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if(request.message === 'get_notes') {
    let getNotes_request = get_records(request.payload)

        getNotes_request.then(res => { // This is where the error occurs, so the above function returns undefined
            chrome.runtime.sendMessage({
                message: 'getNotes_success',
                payload: res
            })
        })
    }
});

function create_database() {
    const request = self.indexedDB.open('MyTestDB');

    request.onerror = function(event) {
        console.log("Problem opening DB.");
    }

    request.onupgradeneeded = function(event) {
        db = event.target.result;

        let objectStore = db.createObjectStore('notes', {
            keypath: "id", autoIncrement: true
        });
        objectStore.createIndex("videoID, videoTime", ["videoID", "videoTime"], {unique: false});
    
        objectStore.transaction.oncomplete = function(event) {
            console.log("ObjectStore Created.");
        }
    }

    request.onsuccess = function(event) {
        db = event.target.result;
        console.log("DB Opened.")
    // Functions to carry out if successfully opened:
    
    // insert_records(notes); This is only done when for the first run, so I will have some notes to use for checking and debugging. The notes are in the form of an array.
    }

}

function get_records(vidID) {
    if(db) {
        const get_transaction = db.transaction("notes", "readonly");
        const objectStore = get_transaction.objectStore("notes");
        const myIndex = objectStore.index("videoID, videoTime");
        console.log("Pre-promise reached!");

        return new Promise((resolve, reject) => {
            get_transaction.oncomplete = function() {
                console.log("All Get Transactions Complete!");
            }

            get_transaction.onerror = function() {
                console.log("Problem Getting Notes!");
            }

            let request = myIndex.getAll(IDBKeyRange.bound([vidID], [vidID, []]));

            request.onsuccess = function(event) {
                console.log(event.target.result);
                resolve(event.target.result);
            }
        });

    }
}

create_database();

现在来看看popup.js代码:

chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
    if (request.message === 'getNotes_success') {
        notesTable.innerHTML = ""; // Clear the table body
        if (request.payload) {
            // Code to display the notes.
        } else {
            // Display a message to add notes or refresh the table.
        }
    }
}

chrome.tabs.query({active: true, currentWindow: true}, (tabs) => {
    site = tabs[0].url; // Variables declared earlier, not shown here

    // Valid YouTube video page
    if (isYTVid.test(site)) { // isYTVid is a previously declared regex string

        refNotesbtn.click(); // Auto-click the refresh notes button
    }

refNotesbtn.addEventListener('click', function() {

    videoID = get_videoID();

    chrome.runtime.sendMessage({
        message: 'get_notes',
        payload: videoID
    });
});

我现在的问题是,上面显示注解的代码在大多数情况下都能正常工作,但如果我切换到另一个标签,然后切换回YouTube标签并打开扩展,检索注解的函数返回undefined,并显示未找到注解的消息。如果我单击按钮刷新注解,它们会正确显示。如果在插入、编辑或删除功能(这里没有显示)中发生此错误,则可能会对用户造成重大问题,因此我希望在继续操作之前解决此错误。
我注意到错误发生时,也没有显示“已达到预先承诺!”的信息,那么get_notes函数是根本没有被触发,还是在触发后才出现问题?代码墙的问题很抱歉,感谢大家的帮助。

hc8w905p

hc8w905p1#

由于db是异步发现的,因此需要缓存Promise-wrapped(例如const dbPromise = ...),而不是原始的db。然后,您可以使用dbPromise.then(db => {...})(或async/await等价物)可靠地访问db
我会这样写,代码中有很多解释性注解。

// background.js

// cache for Promise-wrapped `db` object
let dbPromise = null;

// listener (top-level code)
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
    if(request.message === 'get_notes') {
        if(!dbPromise) {
            dbPromise = create_database()
            .catch(error => {
                console.log(error);
                throw error;
            });
        } else {
            // do nothing ... dbPromise is already cached.
        }
        dbPromise // read the cached promise and chain .then().then() .
        .then(db => get_records(db, request.payload)) // this is where you were struggling.
        .then(res => {
            chrome.runtime.sendMessage({
                'message': 'getNotes_success',
                'payload': res
            });
        })
        .catch(error => { // catch to avoid unhandled error exception
            console.log(error); // don't re-throw
        });
    } else {
        // do nothing ... presumably.
    }
});

function create_database() {
    return new Promise((resolve, reject) => {
        const request = self.indexedDB.open('MyTestDB'); // scope of self?
        request.onerror = function(event) {
            reject(new Error('Problem opening DB.'));
        }
        request.onupgradeneeded = function(event) {
            let db = event.target.result;
            let objectStore = db.createObjectStore('notes', {
                'keypath': 'id', 
                'autoIncrement': true
            });
            objectStore.createIndex('videoID, videoTime', ['videoID', 'videoTime'], {'unique': false});
            objectStore.transaction.oncomplete = function(event) {
                console.log('ObjectStore Created.');
                resolve(db);
            }
        }
        request.onsuccess = function(event) {
            let db = event.target.result;
            console.log('DB Opened.')
            // Functions to carry out if successfully opened:
            
            // insert_records(notes); This is only done when for the first run, so I will have some notes to use for checking and debugging. The notes are in the form of an array.
            resolve(db);
        }
    });
}

function get_records(db, vidID) {
    // first a couple of low-lwvel utility functions to help keep the high-level code clean.
    function getTransaction = function() {
        const transaction = db.transaction('notes', 'readonly');
        transaction.oncomplete = function() {
            console.log('All Get Transactions Complete!');
        }
        transaction.onerror = function() {
            console.log('Problem Getting Notes!');
        }
        return transaction;
    };
    function getAllAsync = function(transaction) {
        return new Promise((resolve, reject) {
            const objectStore = transaction.objectStore('notes');
            let request = objectStore.index('videoID, videoTime').getAll(IDBKeyRange.bound([vidID], [vidID, []]));
            request.onsuccess = function(event) {
                // console.log(event.target.result);
                resolve(event.target.result);
            }
            request.onerror = function(event) { // presumably
                reject(new Error('getAll request failed'));
            }
        }); 
    };
    return getAllAsync(getTransaction(db));
}

我唯一不确定的部分是request.onupgradeneededrequest.onsuccess之间的相互作用。我假设这些事件中的一个或另一个会触发。如果它们按顺序触发(?),那么代码可能需要稍微不同。

cfh9epnr

cfh9epnr2#

我的建议如下:创建一个简单的promise返回helper函数,用于连接indexedDB。

function open(name, version, onupgradeneeded) {
  return new Promise((resolve, reject) => {
    const request = indexedDB.open(name, version);
    request.onupgradeneeded = onupgradeneeded;
    request.onerror = event => reject(event.target.error);
    request.onsuccess = event => resolve(event.target.result);
  });
}

修改get_records以接受数据库参数作为其第一个参数,始终返回promise,并在promise执行器回调函数中执行所有工作。

function get_records(db, vidID) {
  return new Promise((resolve, reject) => {
    const tx = db.transaction("notes", "readonly");
    const store = tx.objectStore("notes");
    const index = store.index("videoID, videoTime");
    let request = index.getAll(IDBKeyRange.bound([vidID], [vidID, []]));
    request.onsuccess = function(event) {
      console.log(event.target.result);
      resolve(event.target.result);
    };
    request.onerror = event => reject(event.target.error);
  });
}

当你需要做某件事时,就撰写作品:

function uponSomethingHappeningSomewhere() {
  open('mydb', 1, myOnUpgradeneeded).then(db => {
    const videoId = 1234;
    return get_records(db, videoId);
  }).then(results => {
    return chrome.runtime.sendMessage({
      'message': 'getNotes_success',
      'payload': res
    });
  }).catch(console.error);
}

相关问题