您的位置:首页 > 数据库

Web SQL Database的异步机制

2017-03-16 01:46 381 查看

概述

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

所有问题都会得到完美、有序的解决。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息