Promise仅在切换制表符后返回未定义



我正在制作一个chrome扩展,允许用户在YouTube视频上做笔记。注释使用IndexedDB存储。我遇到了一个问题,一个承诺返回未定义,如果我切换到另一个选项卡,然后切换回来。首先,我使用的大部分代码使问题更容易理解。

// 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();

现在对于pop .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选项卡并打开扩展,检索笔记的功能返回未定义,并且没有发现笔记的消息显示。如果我单击按钮刷新注释,它们就会正确显示。如果这个错误发生在插入、编辑或删除函数中(这里没有显示),那么它可能会给用户带来一个大问题,所以我想在继续之前解决它。

我注意到,当错误发生时,达到了"预承诺"!消息也没有显示,所以是根本没有触发get_notes函数,还是在触发之后才出现问题?为代码墙道歉,并感谢任何帮助。

由于db是异步发现的,您需要缓存承诺包装的(例如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之间的相互作用。我假设这些事件中的一个或另一个会发生。如果它们按顺序触发(?),那么代码可能需要稍微不同。

我建议如下。创建一个简单的承诺返回帮助函数来连接到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);
}

最新更新