Запрос Javascript WebSQL в цикле for. Как узнать, когда закончил?

Думаю, у меня есть относительно простой вопрос, но я продолжаю мыслить кругами, и даже Google не дает мне ответа, с которым я могу работать.

В основном я пытаюсь скопировать некоторые записи, которые хранятся локально с помощью WebSQL. Копирование не является проблемой, но мне нужно знать, когда все действия копирования завершены, прежде чем моя программа сможет продолжить работу.

Вызовы WebSQL выполняются асинхронно, поэтому единственный способ для меня сделать это — использовать функцию обратного вызова. Однако, поскольку запросы выполняются в цикле for, я не могу использовать функцию обратного вызова, поскольку она будет срабатывать при первом завершенном запросе, как показано в коде.

Код выглядит следующим образом:

function copyRecords(old_parent_id, new_parent_id, callback){
    var db = openDatabase('test', '1.0', 'test', 50 * 1024 * 1024);
    db.transaction(function (tx) {
        tx.executeSql('SELECT * FROM table WHERE parent_id = ?', [old_parent_id], function(tx, results){
            for(var i = 0; i < results.rows.length; i++){
                db.transaction(function (tx2) {
                    tx2.executeSql('INSERT INTO table (name, parent_id) VALUES (?, ?)', [results.rows.item(i).name, new_parent_id], callback);
                })
            }
        });
    });
}

Я также пробовал вызывать функцию обратного вызова, когда i == results.rows.length, но это не гарантирует, что все запросы будут выполнены.

Я предполагаю, что некоторые из вас сталкивались с той же проблемой раньше, поэтому любая помощь в том, как решить эту проблему и убедиться, что функция обратного вызова вызывается только после завершения цикла for, очень ценится.

Заранее спасибо.


person user1749815    schedule 16.10.2012    source источник
comment
Возможно ли вывести внутреннюю транзакцию из цикла через результаты, чтобы цикл INSERT полностью проходил внутри транзакции?   -  person Stan    schedule 16.10.2012


Ответы (2)


Обычным подходом для этого было бы использование рекурсивного асинхронного обратного вызова для обработки каждой отдельной записи вместо цикла for.

Пока осталось больше записей, асинхронный обратный вызов вызывает сам себя. Когда записей не осталось, он может вызвать предоставленный вами обратный вызов.

Код ниже заменит содержимое вашего внутреннего обработчика обратного вызова:

(function nextRecord() {
    var row = results.rows.shift();
    if (row) {
        db.transaction(function (tx2) {
            tx2.executeSql('INSERT INTO table (name, parent_id) VALUES (?, ?)',
                [row.item(i).name, new_parent_id], nextRecord);
       });
    } else {
        callback();
    }
})();
person Alnitak    schedule 17.10.2012
comment
Спасибо за ваш ответ! Я никогда не думал об использовании рекурсивных функций! - person user1749815; 18.10.2012
comment
спасибо за идею, но кажется, что результаты web-sql не поддерживают функцию shift(), так как это не массив. Поэтому мне пришлось увеличить счетчик вручную, и это сработало! - person Matthieu; 20.02.2013

Это лучше всего сделать, подсчитывая количество выполнений функции «обратного вызова», и продолжая только после того, как она достигнет полного количества результирующего набора.

Вот ваш код с изменениями:

function copyRecords(old_parent_id, new_parent_id, callback){
    var db = openDatabase('test', '1.0', 'test', 50 * 1024 * 1024);
    db.transaction(function (tx) {
        tx.executeSql('SELECT * FROM table WHERE parent_id = ?', [old_parent_id], function(tx, results){
            if (results.rows.length == 0) 
                callback(); // don't forget this case!
            else {
                var nbrInserted = 0; // This will keep track of how many have been inserted
                for(var i = 0; i < results.rows.length; i++){
                    db.transaction(function (tx2) {
                        tx2.executeSql('INSERT INTO table (name, parent_id) VALUES (?, ?)', [results.rows.item(i).name, new_parent_id], function() {
                            ++nbrInserted; // increment this for every insert
                            if (nbrInserted == results.rows.length) // check if complete
                                callback(); // Do your callback.
                        });
                    });
                }
            }
        });
    });
}

Что касается меня, то я обнаружил, что асинхронные API-интерфейсы WebSQL несколько громоздки, и, поскольку базы данных WebSQL, скорее всего, исчезнут (от стандарта отказались), я бы порекомендовал всем перейти на SequelSphere. Это реляционная база данных HTML5/JavaScript, которая работает во всех браузерах и на всех платформах. Он также хранит данные в localStorage, предоставляя ему все преимущества WebSQL без каких-либо проблем.

Дайте мне знать, если приведенное выше решение не работает для вас.

Удачи!

Джон...

person John Fowler    schedule 17.10.2012
comment
Вы действительно должны были заявить о своем интересе к SequelSphere. - person Alnitak; 17.10.2012
comment
Хорошая точка зрения. Ты совершенно прав. Пожалуйста, простите... Я подключен к SequelSphere и не могу не дать вилку. - person John Fowler; 17.10.2012
comment
p.s. асинхронные обратные вызовы не громоздки — смотрите мой ответ, как это сделать правильно! - person Alnitak; 17.10.2012
comment
p.p.s. async очень громоздкий и крайне необходимый. Только мое мнение. Мне очень нравится ваш ответ, но если я правильно его прочитал, ваш ответ будет ошибочным, если из выбора не будут возвращены строки. - person John Fowler; 17.10.2012
comment
Дох! ... нажал... и исправил. - person John Fowler; 17.10.2012