WebSQL事务不会在JS回调函数中运行

Bra*_*oke 4 javascript jquery cordova

我正在使用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);

}
Run Code Online (Sandbox Code Playgroud)

运行上面的函数会在上面提到的行上给出错误"INVALID_STATE_ERR:DOM Exception 11".当我实际尝试使用返回的JSON数据插入数据时,它会做同样的事情.我也尝试了$ .getJSON技术,结果完全相同.

任何意见,将不胜感激!

Cel*_*ada 6

虽然接受的答案是正确的,但我想扩展它,因为我遇到了同样的问题,并且答案没有说明为什么它不像OP那样有效.

在Web SQL中创建事务时,只有在事务中排队的任何语句时,事务处理才会保持活动状态.一旦事务中的语句管道干涸,引擎就会关闭(提交)事务.这个想法是,当function(tx) { ... }回调运行时,

  1. 它执行所需的所有语句.executeSql是异步的,因此即使语句尚未执行,它也会立即返回.
  2. 它将控制权返回给Web SQL引擎.

此时,引擎注意到有一些语句排队并在关闭事务之前将它们运行完成.在你的情况下,会发生什么:

  1. 你调用executeSql两次来排队两个语句.
  2. 你通过ajax请求一些东西.
  3. 你回来了

引擎运行它排队的两个语句.ajax请求也是异步运行的,但它必须访问速度很慢的网络,因此可能还没有完成.此时,语句队列为空,Web SQL引擎决定是时候提交并关闭事务了!它无法知道将来会有另一个声明!当ajax调用返回并且它尝试执行时INSERT INTO locations,为时已晚,事务已经关闭.

接受的答案建议的解决方案有效:不要在ajax回调中使用相同的事务,而是创建一个新事务.不幸的是,它有使用2个事务而不是1的预期陷阱:操作不再是原子的.这可能对您的申请有用,也可能不重要.

如果交易的原子性对您很重要,那么您只有2个资源:

  • 在ajax回调中的一个事务中执行所有操作(所有3个语句).

    这是我的建议.我认为在创建表之前等待ajax请求完成后很可能与您的应用程序要求兼容.

  • 按照此处的说明同步执行ajax请求.

    我不推荐.在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, .... ]
        )
    });
});
Run Code Online (Sandbox Code Playgroud)

问题在于,对于承诺,链式.then()条款不会在解除原始承诺后立即运行.它只排队等待以后执行,就像ajax请求一样.唯一的区别是,与慢速ajax请求不同,该.then()子句几乎立即运行.但"差不多"还不够好:它可能会或者可能不会很快运行,以便在事务关闭之前将下一个SQL语句放入队列中; 因此,代码可能会或可能不会产生取决于时序和/或浏览器实现的无效状态错误.

太糟糕了:Promise在SQL事务中使用会很有用.上面的伪示例可以很容易地在没有promises的情况下被重写,但是一些用例可以极大地利用许多.then()s 的链以及类似的东西Promise.all可以确保整个SQL语句集合以任何顺序运行但是在其他声明.