Транзакция WebSQL не будет выполняться в функциях обратного вызова JS - PullRequest
4 голосов
/ 31 марта 2012

Я использую 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 Exception 11» в строке, указанной выше. Это делает то же самое, когда я пытаюсь использовать возвращенные данные JSON для вставки данных. Я также попробовал метод $ .getJSON с точно таким же результатом.

Любой совет будет оценен!

Ответы [ 3 ]

6 голосов
/ 27 ноября 2014

Хотя принятый ответ правильный, я хотел бы остановиться на нем, потому что столкнулся с той же проблемой, и этот ответ не говорит , почему он не работает какУ 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 ресурса:

  • Делать все (все 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 будет выполняться в любомзаказать, но все завершить до какого-либо другого утверждения.

3 голосов
/ 06 июня 2012

Сначала я бы предложил не называть вашу базу данных 'tx', а, скорее, db или database.Это может быть проблемой именования переменных, поскольку и параметр функции, и переменные вашей базы данных называются «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 не являются синхронными.

0 голосов
/ 16 сентября 2015

У нас есть способ заблокировать транзакцию, когда вы выполняете любую AJAX или другую асинхронную операцию.По сути, перед вызовом AJAX вам нужно запустить фиктивную операцию БД и, в случае успеха этой операции, проверить, выполняется ли AJAX или нет, и снова вызывать ту же фиктивную операцию, пока ваш AJAX не будет выполнен.Когда AJAX завершен, вы можете повторно использовать объект транзакции для следующего набора executeSQL.Этот подход подробно объясняется в этой статье здесь .(Я надеюсь, что кто-то не удалит этот ответ тоже, как кто-то ранее сделал по аналогичному вопросу)

Попробуйте этот подход

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...