Web SQL Database的异步机制

本文介绍WebSQLDatabase异步查询机制,通过示例代码详细解释如何正确使用transaction及executeSql函数,避免常见错误,实现高效并发查询。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

概述

Web SQL Database是一个主要基于异步的实现。其原理是, 发起SQL语句命令后, 不待结果传回, 立即将程序流程控制权转交给下一语句。SQL语句执行完毕后, 单独线程再调用回调函数, 返回查询结果。

什么意思呢? 看看下面的伪码:

var id = executeSql("SELECT ID FROM tableA");
executeSql("SELECT * FROM tableB WHERE ID = ?", [id]);

我们需要先从表A中取出一个ID值并赋值于id, 接着, 希望使用这个id来进行下一步的查询。

由于异步工作机制, 这段代码是无法工作的。当第一个executeSql()语句发起后, 还未等结果出来, 就立即执行第二条executeSql()语句了。此时, id的状态是undefined, 当然无法进行正确的查询。

正确的做法应是将第二条语句放在一个回调函数中执行, 这样可确保只有等第一个查询结果出来后才执行第二条语句。

executeSql("SELECT ID FROM tableA", function(resultSet) {
    var id = resultSet.rows.item(0).ID;
    executeSql("SELECT * FROM tableB WHERE ID = ?", [id])
});

这样, 就造成了嵌套会越来越长。且由于再加上很容易混淆的transaction()函数, 编写这样的代码又臭又长, 即难看,又容易出错。

本文通过简要地说明transaction()及executeSql()各个函数参数的作用, 从最简单的需求开始, 渐渐细化为功能较为齐全且代码结构清晰的一个解决办法。

关于transaction及readTransaction

void transaction(in SQLTransactionCallback callback, in optional SQLTransactionErrorCallback errorCallback, in optional SQLVoidCallback successCallback);

interface SQLTransactionCallback {
  void handleEvent(in SQLTransaction transaction);
};

interface SQLTransactionErrorCallback {
  void handleEvent(in SQLError error);
};

interface SQLVoidCallback {
  void handleEvent();
};

第一个SQLTransactionCallback用于使用所传入的tx。第二个可显示错误信息。第三个是当处理完整个transaction时, 统一回调callback函数。

关于executeSql函数

void executeSql(in DOMString sqlStatement, in optional ObjectArray arguments, in optional SQLStatementCallback callback, in optional SQLStatementErrorCallback errorCallback);

interface SQLStatementCallback {
  void handleEvent(in SQLTransaction transaction, in SQLResultSet resultSet);
};

interface SQLStatementErrorCallback {
  boolean handleEvent(in SQLTransaction transaction, in SQLError error);
};

由于执行Sql语句时, 有时需要返回查询结果, 因此SQLStatementCallback有一SQLResultSet的形参。可在这里将查询结果改为程序所需的各种形式。

结合使用transaction及executeSql

一个transaction可一并使用多个executeSql。因此, 可将多个Sql语句放在一个transaction执行完毕后, 整合多个Sql语句的结果, 再将该结果作为参数来调用transaction的successCallback。这样做,可较完美地利用并发机制。

db.transaction(
    function(tx) {},     // use tx
    function(error) {},  // handle error if any
    function() {}        // this callback will be called if the transaction is committed
);

再进一步:

db.transaction(
    function(tx) {
        tx.executeSql();
        tx.executeSql();
        ...
    },
    function(error) {},  // handle error if any
    function() {}        // call this callback if the transaction is committed
);

现在,如何将tx.executeSql()的结果传给最后的函数里呢? 毕竟该函数没有形参。很简单, db.transaction()虽然很长, 但它毕竟只是一个函数。在此函数前声明一个变量, 用于保存tx.executeSql()的结果, 这样, 第三个回调函数也可以使用该结果了。

var result = {};

db.transaction(
    function(tx) {
        tx.executeSql(
          "SELECT ID FROM ... WHERE ID = ?",
          [1],
          function(tx, resultSet) {
            result.id = resultSet.rows.item(0).ID;  // save the selected ID in result.id
          },
          function(tx, error) {
            console.log(error.code + error.message);
          }
        );

        tx.executeSql();
        ...
    },
    function(error) {},  // handle error if any
    function() {}        // call this callback if the transaction is committed
);

tx.executeSql()共有4个参数。在第三个参数(该参数为函数)中, resultSet是执行查询完毕后所传回的结果, 可在这个函数中将查询结果保存至前面所声明的变量。第四个函数参数, 可用于即时检查执行Sql语句可能出现的错误, 如, Sql语句语法错误, 或往数据库中插入数据时, 出现了违反了键值唯一的错误,等等。这一步不要轻易省略, 可大大缩短调试时间。

第二个tx.executeSql()可灵活地执行另一相关的Sql语句。例如, 查询并返回多项记录。

var result = {};

db.transaction(
    function(tx) {
        tx.executeSql(
          "SELECT ID FROM ... WHERE ID = ?",
          [1],
          function(tx, resultSet) {
            result.id = resultSet.rows.item(0).ID;  // save the selected ID in result.id
          },
          function(tx, error) {
            console.log(error.code + error.message);
          }
        );

        tx.executeSql(
          "SELECT * FROM customer",
          [],
          function(tx, resultSet) {
            result.resultSet = resultSet;  // save the whole resultSet in result.resultSet
          },
          function(tx, error) {
            console.log(error.code + error.message);
          }
        );
        ...
    },
    function(error) {},  // handle error if any
    function() {}        // call this callback if the transaction is committed
);

如果事务执行过程中有任一错误, 就会将error传回db.transaction()的第三个函数参数。

var result = {};

db.transaction(
    function(tx) {
        tx.executeSql(
          "SELECT ID FROM ... WHERE ID = ?",
          [1],
          function(tx, resultSet) {
            result.id = resultSet.rows.item(0).ID;  // save the selected ID in result.id
          },
          function(tx, error) {
            console.log(error.code + ", " + error.message);
          }
        );

        tx.executeSql(
          "SELECT * FROM customer",
          [],
          function(tx, resultSet) {
            result.resultSet = resultSet;  // save the whole resultSet in result.resultSet
          },
          function(tx, error) {
            console.log(error.code + error.message);
          }
        );
        ...
    },

    function(error) {
        console.log("transaction error = " + error.code + ", " + error.message);
    },

    function() {}        // call this callback if the transaction is committed
);

最后一步, 可在db.transaction()的第四个函数参数中使用上面声明的变量了。

var result = {};

db.transaction(
    function(tx) {
        tx.executeSql(
          "SELECT ID FROM ... WHERE ID = ?",
          [1],
          function(tx, resultSet) {
            result.id = resultSet.rows.item(0).ID;  // save the selected ID in result.id
          },
          function(tx, error) {
            console.log(error.code + ", " + error.message);
          }
        );

        tx.executeSql(
          "SELECT * FROM customer",
          [],
          function(tx, resultSet) {
            result.resultSet = resultSet;  // save the whole resultSet in result.resultSet
          },
          function(tx, error) {
            console.log(error.code + error.message);
          }
        );
        ...
    },

    function(error) {
        console.log("transaction error = " + error.code + ", " + error.message);
    },

    function() {
        console.log(result.id);
        console.log(result.resultSet);
    }
);

当然, 如果嫌这个函数过长, 可将第四个函数参数分解为另一个单独的函数。

function doQueris() {
    var result = {};

    db.transaction(
        function(tx) {
            tx.executeSql(
              "SELECT ID FROM ... WHERE ID = ?",
              [1],
              function(tx, resultSet) {
                result.id = resultSet.rows.item(0).ID;  // save the selected ID in result.id
              },
              function(tx, error) {
                console.log(error.code + ", " + error.message);
              }
            );

            tx.executeSql(
              "SELECT * FROM customer",
              [],
              function(tx, resultSet) {
                result.resultSet = resultSet;  // save the whole resultSet in result.resultSet
              },
              function(tx, error) {
                console.log(error.code + error.message);
              }
            );
            ...
        },

        function(error) {
            console.log("transaction error = " + error.code + ", " + error.message);
        },

        function() {
            onQuerriesFinished(result);
        }
    );
}

function onQueriesFinished(result) {
    console.log(result.id);
    console.log(result.resultSet);
}

这里有两个函数: doQueris()及onQueriesFinished()。变量result成为doQueris()的局部变量。在doQueris()函数的事务执行完毕后, 通过传递result局部变量给onQueriesFinished()这个回调函数, 从而实现了将查询结果在不同的函数之间进行传递的效果。

最后, 只需简单地发起查询命令:

doQueris();

所有问题都会得到完美、有序的解决。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值