Хотя принятый ответ правильный, я хотел бы остановиться на нем, потому что столкнулся с той же проблемой, и этот ответ не говорит , почему он не работает какУ OP было это.
Когда вы создаете транзакцию в Web SQL, обработка транзакции остается активной только до тех пор, пока есть какие-либо операторы, поставленные в очередь в транзакции .Как только поток операторов в транзакции иссякает, механизм закрывает (фиксирует) транзакцию.Идея состоит в том, что при выполнении обратного вызова function(tx) { ... }
- выполняет все необходимые ему операторы.
executeSql
является асинхронным, поэтому возвращается немедленно, даже если оператор еще не выполнен. - Возвращает управление обратно механизму Web SQL.
На этом этапе ядрозамечает, что в очереди есть операторы, и завершает их до закрытия транзакции.В вашем случае происходит следующее:
- Вы дважды звоните
executeSql
, чтобы поставить в очередь два оператора. - Вы запрашиваете что-то через ajax.
- Вы возвращаете
Двигатель запускает два оператора, которые он поставил в очередь.Запрос ajax также выполняется асинхронно, но он должен получить доступ к сети, которая работает медленно, поэтому, вероятно, она еще не завершена.На этом этапе очередь операторов пуста, и механизм Web SQL решает, что пришло время зафиксировать и закрыть транзакцию!У него нет никакого способа узнать, что будет другое заявление, прибывающее позже!К тому времени, когда вызов ajax возвращается и он пытается выполнить INSERT INTO locations
, уже слишком поздно, транзакция уже закрыта.
Решение, предлагаемое принятым ответом, работает: не используйте ту же транзакцию внутриобратный вызов ajax, но создайте новый.К сожалению, у него есть подводный камень, который можно ожидать от использования 2 транзакций вместо 1: операция больше не является атомарной.Это может или не может быть важным для вашего приложения.
Если для вас важна атомарность транзакции, вы можете использовать только 2 ресурса:
Делать все (все 3операторов) в одной транзакции внутри обратного вызова ajax.
Это то, что я рекомендую.Я думаю, что весьма вероятно, что ожидание до завершения запроса 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, .... ]
)
});
});
Проблема заключается в том, что с обещаниями цепочка .then()
не выполняется сразу после разрешения исходного обещания.Он ставится только в очередь для последующего выполнения, так же, как запрос ajax.Единственное отличие состоит в том, что, в отличие от медленного ajax-запроса, предложение .then()
выполняется почти сразу.Но «почти» недостаточно хорошо: он может запускаться или не выполняться достаточно быстро, чтобы вставить следующий оператор SQL в очередь до закрытия транзакции;соответственно, код может или не может выдавать ошибку недопустимого состояния в зависимости от времени и / или реализации браузера.
Слишком плохо: Promise
было бы полезно использовать внутри транзакций SQL.Приведенный выше псевдо-пример может быть легко переписан без обещаний, но в некоторых случаях использования могут быть использованы цепочки из множества .then()
, а также такие вещи, как Promise.all
, которые могут гарантировать, что вся коллекция операторов SQL будет выполняться в любомзаказать, но все завершить до какого-либо другого утверждения.