gpt4 book ai didi

javascript - IndexedDB-打开数据库后成功后无法传递变量

转载 作者:行者123 更新时间:2023-11-30 10:08:34 27 4
gpt4 key购买 nike

我一直在寻求有关为Web存储设置IndexedDB的帮助,但遇到了一个我找不到好的答案的问题。成功设置/打开数据库后,我很难传递包含数据库信息的变量以供以后访问。这是我的代码,我一直在遵循MDN的指南:

const DB_NAME = 'database-name';
const DB_VERSION = 2;
const DB_STORE_NAME = 'users';

var db;

function openDb() {
console.log('openDb ...')
var request = indexedDB.open(DB_NAME, DB_VERSION);

request.onsuccess = function(event) {
db = request.result;
console.log("openDb DONE");
};

request.onerror = function(event) {
console.log(request.errorCode);
};

request.onupgradeneeded = function(event) {
console.log('openDb.onupgradeneeded');
var store = event.currentTarget.result.createObjectStore(DB_STORE_NAME, { keyPath: 'id', autoIncrement: true });

store.createIndex('age', 'age', { unique: false });
};
}

function getObjectStore(store_name, mode) {
var tx = db.transaction(store_name, mode);
return tx.objectStore(store_name);
}


调用 getObjectStore时,变量 db是不确定的。我对javascript的了解非常有限,有些概念我也没有。该指南未显示正在执行的任何特殊操作,其演示仍按原样进行。其他一些指南提到了实现回调,但是它们没有显示其完成方式,也不了解回调的概念。任何帮助表示赞赏。谢谢。

最佳答案

不幸的是,在继续使用indexedDB之前,您需要了解一个相对复杂的概念,通常称为异步JavaScript。关于stackoverflow的AJAX已有数千个问题。我正在尝试以最有礼貌的方式讲这句话,但是基本上,您正在寻找的答案已经由其他问题和许多其他网站提供。不过,这里有一些快速提示。

首先,您的方法永远行不通。您不能跳过关于异步的学习。

其次,不要使用setTimeout技巧来使其正常工作。那是可怕的建议。

第三,在一般级别上,当函数以特定方式使用时,回调仅是用于描述函数的单词。具体来说,回调是指一个函数,该函数作为参数传递给另一个函数,然后另一个函数可能会在稍后的某个时间点调用该函数。更具体地说,回调通常是一个在函数完成后在传递给函数的末尾调用的函数。

例如:

function a(b) { alert(b); }
function c(d) { d('hi'); }
c(a);


乍一看可能有点令人困惑,但这是我可以简单描述的最简单的方法。在示例中,最后一行调用函数c并传入函数a。该代码的作用是您将“ hi”视为浏览器警报。在此示例中,函数a作为参数/参数传递给函数c。函数c将名称d用作其唯一参数。 c用字符串“ hi”调用d。在描述此示例时,我们可以说参数d表示传递给函数c的回调函数。我们也可以说函数a是函数c使用的特定回调函数。基本上就是这样。当您传入一个函数作为参数,而另一个函数调用传入的参数时,您正在使用回调。

然后事情变得更加复杂,因为您必须学习如何读取和编写异步代码。正确地介绍它需要花费几页。这是一个极端的崩溃过程。
传统上您一直在编写同步代码,即使您未调用它也是如此。同步代码按照编写语句的顺序准确地在您期望的时间运行。这是典型同步代码的简短示例:

function sum(a, b) { return a + b; }
alert(sum(1, 2));


简单的东西。下一个示例是使用回调但仍处于同步状态的代码。

function doOperation(op, num1, num2) { return op(num1, num2); }
function sumOperation(num1, num2) { return num1 + num2; }
var result = doOperation(sumOperation, 1, 2);
alert(result);


在这里,我们将sumOperation函数传递给doOperation函数。 sumOperation是回调函数。这是名称为“ op”的第一个参数。还是很简单的东西。现在考虑下一个示例。下一个示例的要点是说明我们如何将控制传递给函数以执行某些操作。有点像goto / labels的工作方式。

function doOperation(op, num1, num2) {
var result = op(num1, num2);
alert(result);
return undefined;
}
function sumOperation(num1, num2) { return num1 + num2; }
doOperation(sumOperation, 1, 2);


注意doOperation如何不再返回值。它在其功能体内具有逻辑。因此,一旦我们调用doOp,浏览器就会开始在doOperation中运行代码。因此,我们从外部上下文切换到函数主体。另外,由于doOperation不返回任何内容,因此我们无法对其返回值执行任何操作。逻辑被锁定在doOperation函数的主体内部。该代码仍然可以正常工作,只是现在我们不从doOperation返回任何东西,现在逻辑位于doOperation内部,而不是在main / global上下文中。

现在是一个使用setTimeout的示例。这与使用setTimeout的建议完全无关。

function doOperation(op, num1, num2) {
setTimeout(function runLater() {
var result = op(num1, num2);
alert(result);
return undefined;
}, 1000);
return undefined;
}
function sumOperation(num1, num2) { return num1 + num2; }
doOperation(sumOperation, 1, 2);


这里的要点是要了解我们使用了一个回调(在此示例中名为runLater),并且回调内的代码不会立即运行。因此,我们不能再说它是同步运行的。相反,我们将构成回调函数主体的语句称为异步。因此,现在1秒后出现警报。请注意,我们如何无法从runLater返回任何内容。还要注意我们如何无法从doOperation返回任何信息。没有任何回报。无法将'result'变量中的值移出runLater的范围。它被锁定在那里。

让我们尝试几乎相同的事情,但是尝试让runLater设置一个变量。另外,我将省略“ return undefined”,因为这是每个没有显式return语句的函数都会返回的内容。

var aGlobalResult = null;
function doOperation(op, num1, num2) {
setTimeout(function runLater() {
aGlobalResult = op(num1, num2);
}, 1000);
}
function sumOperation(num1, num2) { return num1 + num2; }
doOperation(sumOperation, 1, 2);
alert(aGlobalResult);


希望您能解决这个问题。首先,runLater不返回任何内容,因此doOperation不返回任何内容,因此我们甚至无法尝试执行 aGlobalResult = doOperation(...);之类的操作,因为这毫无意义。其次,这里的结果是您将看到警报“未定义”,因为警报语句在为aGlobalResult分配值的语句之前执行。即使您在代码的较高位置(较早)编写了赋值语句,也较晚才发出警报。这是一些新开发人员在这里碰到的砖墙。这确实使一些人感到困惑。 aGlobalResult在此处未定义,因为setTimeout直到以后才进行设置。即使我们将0毫秒传递给setTimeout,它仍然是“较晚的”,这意味着分配发生在警报之后的较晚时间点。警报消息将始终是未定义的。您绝对无法做任何事情来避免这种工作方式。没有。期。别再尝试了学习它,或者完全放弃。

因此,通常如何编写行为或涉及异步内容的代码?通过使用回调。再次意味着您不能再使用return语句将值分配给外部作用域变量。相反,您想编写函数并将控制权传递给各种函数。换句话说,代替:

function a() {}
function b() {}
function c() {}
a(); b(); c();


您编写这样的代码:

function a(callback) {  
var asdf = 1+2; // do some stuff in a
alert('a finished');
// a has now completed, call its callback function, appropriately named callback
callback();
}

function b(callback) {
var asdfasdfasdf = 3 + 4;
alert('b finished');

// call the callback
callback();
}

a( function(){ b(function() { alert('both a and b finished'); }); });


这更正式地称为 continuation passing style或CPS。

因此,这是编写回调函数和基本异步代码的最基本示例。现在您可以开始使用indexedDB。您会注意到的第一件事是indexedDB.open函数被记录为异步的。那么,我们如何使用它呢?像这样:

var someGlobalVariable = null;

var openRequest = indexedDB.open(...);
openRequest.onsuccess = function openRequestOnSuccessCallbackFunction(event) {
// Get a reference to the database variable. If this function is ever called at some later
// point in time, the database variable is now defined as the 'result' property of the
// open request. There are multiple ways to access the result property. Any of the following
// lines works 100% the same way. Use whatever you prefer. It does not matter.
var databaseConnection = openRequest.result;
var databaseConnection = this.result;
var databaseConnection = event.target.result;
var databaseConnection = event.currentTarget.result;


// Now that we have a valid and defined databaseConnection variable, do something with it
// e.g.:
var transaction = databaseConnection.transaction(...);
var objectStore = transaction.objectStore(...);
// etc.

// DO NOT DO THE FOLLOWING, it does not work. Why? Review the early descriptions. First off
// this onsuccess callback function does not return anything. Second off this function gets
// called at some *later* point in time, who knows when. It could be a nanosecond later.
someGlobalVariable = databaseConnection;
};


希望这会让您踏上前进的道路。

编辑:我想我会增加一些介绍。您需要了解的一个有关概念的知识,我对控制的解释不够清楚,这是命令式和声明式编程之间的区别。

命令式编程涉及按您编写的顺序执行一系列语句。您是呼叫者并且处于控制之中。命令式代码如下(虚构代码):

var dbConn = dbFactory.newConnection('mydb');
var tx = dbConn.newTransaction();
var resultCode = tx.put(myObject);
if(resultCode == dbResultConstants.ERROR_PUT_KEY_EXISTS) {
alert('uhoh');
}


声明式编程略有不同。使用声明性方法,您可以编写函数,然后将这些函数注册(即挂钩或绑定)到JavaScript引擎,然后在适当的时候稍后再由该引擎运行您的代码。引擎是调用者,而是控制者,而不是您。声明式编程涉及回调,并且看起来像这样(虚拟代码):

dbFactory.newConnection(function onConnect(dbConn) {
dbConn.newTransaction(function onNewTransaction(tx) {
tx.put(myObject, function onPut(resultCode) {
if(resultCode == dbResultConstants.ERROR_PUT_KEY_EXISTS) {
alert('uhoh');
}
});
});
});


在此示例中,您仅调用了虚构的dbFactory.newConnection函数。您传入了回调函数。您没有自己调用回调函数。引擎调用回调函数。您不能自己调用​​回调函数。这就是为什么JavaScript引擎可以允许您编写异步代码的全部想法。因为您无法控制语句/函数的执行顺序。引擎开始控制它。您要做的就是编写函数,注册它们,然后启动一连串的回调(唯一的命令性行,starting语句)。

因此,这就是为什么您的问题中的 getObjectStore之类的功能将不起作用的原因。您正在尝试自己调用该函数,但这是倒退的。您只能编写一个函数并进行注册(以某种方式将其作为回调连接到某个地方),然后引擎(而不是您)在以后的某个时间点调用它。

希望这不会造成更多的混乱,但是如果您确实希望通过将数据库变量作为第一个参数传递给函数,则可以实际编写函数 getObjectStore。这引出了逻辑上的下一个问题,即如何获取有效的数据库变量以传递给函数。您不能在全球范围内可靠地获得一个。因为连接变量仅在onOpen回调函数的上下文内有效。因此,您必须从onOpen函数中调用此函数。就像是:

function getObjectStore(db, name, mode) {
var tx = db.transaction(name, mode);
var store = tx.objectStore(name);
return store;
}

var openRequest = indexedDB.open(...);
openRequest.onsuccess = function onOpen(event) {
// get the connection variable. it is defined within this (onOpen) function and open.
var db = this.result;

// call our simple imperative helper function to get the users store. only call it from
// within this onOpen function because that is the only place we can get the 'db' variable.
var usersStore = getObjectStore(db, 'users', 'readwrite');

// do something here with usersStore, inside this function only.
};

关于javascript - IndexedDB-打开数据库后成功后无法传递变量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27806141/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com