我正在使用PhoneGap和jQuery Mobile。我试图从远程位置获取一些JSON数据,然后用它填充本地WebSQL数据库。这是我的JS功能:
function getLocations() {
var tx = window.openDatabase('csdistroloc', '1.0', 'Distro DB', 1000000);
tx.transaction(function(tx) {
tx.executeSql('DROP TABLE IF EXISTS locations'); //this line works!
tx.executeSql('CREATE TABLE IF NOT EXISTS locations (id, name, address, postalcode, phone, category)'); //this line works!
$.ajax({
url: "http://mydomain.com/api.php",
dataType: 'json',
data: { action: "getlocations" },
success: function(data) {
tx.executeSql("INSERT INTO locations (id, name, address, postalcode, phone, category) VALUES (2,'cheese','232','seven',5,6)"); //this line produces an error
}});
}, dberror, dbsuccess);
}
运行上面的函数会在上面提到的行上给出错误“INVALID_STATE_ERR:DOM异常11”。当我实际尝试使用返回的JSON数据插入数据时,它也会做同样的事情。我也尝试了$ .getJSON技术,结果完全相同。
任何建议都将不胜感激!
答案 0 :(得分:6)
虽然accepted answer是正确的,但我想扩展它,因为我遇到了同样的问题而且答案并没有说为什么它不起作用OP就是这样。
在Web SQL中创建事务时,只有there are any statements queued up in the transaction,事务处理才会保持活动状态。一旦事务中的语句管道干涸,引擎就会关闭(提交)事务。这个想法是当function(tx) { ... }
回调运行时,
executeSql
是异步的,因此即使语句尚未执行,它也会立即返回。此时引擎注意到有一些语句排队并在关闭事务之前将它们运行完成。在你的情况下,会发生什么:
executeSql
两次以排队两个语句。引擎运行已排队的两个语句。 ajax请求也是异步运行的,但它必须访问速度很慢的网络,因此可能还没有完成。此时,语句队列为空,Web SQL引擎决定提交和关闭事务的时间!它无法知道将来会有另一个声明!当ajax调用返回并且它尝试执行INSERT INTO locations
时,为时已晚,该事务已经关闭。
接受的答案建议的解决方案有效:不要在ajax回调中使用相同的事务,但要创建一个新事务。不幸的是,它有使用2个事务而不是1的预期陷阱:操作不再是原子的。这对您的申请可能有用,也可能不重要。
如果交易的原子性对您很重要,那么您只有两个资源:
在ajax回调中的一个事务中执行所有(所有3个语句)。
这是我推荐的。我认为,在创建表之前等待ajax请求完成后,很可能会与您的应用程序要求兼容。
同步执行ajax请求as explained here。
我不推荐。 JavaScript中的异步编程是一个好的事物。
顺便说一句,我在Promises的上下文中遇到了这个问题,代码看起来像这样:
// XXX don't do this, it doesn't work!
db.transaction(function(tx) {
new Promise(function(resolve, reject) {
tx.executeSql(
"SELECT some stuff FROM table ....", [],
function(tx, result) {
// extract the data that are needed for
// the next step
var answer = result.rows.item( .... ).some_column;
resolve(answer);
}
)
}).then(function(answer) {
tx.executeSql(
"UPDATE something else",
// The answer from the previous query is a parameter to this one
[ ... , answer, .... ]
)
});
});
问题在于,使用promises,链接的.then()
子句在解析原始promise时不会立即运行。它只排队等待以后执行,就像ajax请求一样。唯一的区别是,与慢速ajax请求不同,.then()
子句几乎立即运行。但是"几乎"不够好:它可能会或可能不会很快运行,以便在事务关闭之前将下一个SQL语句放入队列中;因此,代码可能会或可能不会产生无效状态错误,具体取决于时间和/或浏览器实现。
太糟糕了:Promise
在SQL事务中使用会很有用。上面的伪示例可以很容易地在没有promises的情况下重写,但是一些用例可以极大地利用许多.then()
的链以及Promise.all
这样可以确保整个SQL集合的东西。语句以任何顺序运行,但在其他语句之前完成。
答案 1 :(得分:3)
我首先建议不要命名数据库'tx',而是命名数据库或数据库。这可能是一个变量命名问题,因为函数参数和数据库变量都被称为“tx”
编辑:我遇到了同样的问题并通过在回调中进行查询来解决它自己的事务。像这样:success: function(data) {
tx.transaction(function(transaction){
transaction.executeSql("INSERT INTO locations (id, name, address, postalcode, phone, category)
VALUES (2,'cheese','232','seven',5,6)"); //now more DOM exception!
}
}}
我认为问题是在回调被触发时外部事务已经完成,因为webSQL的事务不是同步的。
答案 2 :(得分:0)
我们确实有办法锁定事务,同时执行任何AJAX或其他异步操作。基本上在调用AJAX之前,你需要启动一个虚拟db操作,并且在该操作成功时检查AJAX是否完成,并再次调用相同的虚拟操作,直到你的AJAX完成。当AJAX完成后,您现在可以重用事务对象来执行下一组executeSQL。在article here中详细解释了这种方法。 (我希望有人不会删除这个答案,就像之前有人在类似问题上所做的那样)