HTML5 Web SQL Transactions пропустили без ошибок при срабатывании сенсорного экрана в IOS

У меня возникают проблемы с транзакциями базы данных на устройствах IOS. Если пользователь не прикасается к телефону, все работает так, как ожидалось. Если пользователь нажимает/прокручивает/затрагивает экран, некоторые транзакции напрямую вызывают их successCallback, не вызывая обратного вызова фактической транзакции.

Упрощенный пример здесь: http://jsfiddle.net/Tk9rv/

Чтобы проверить, просто откройте http://jsfiddle.net/Tk9rv/embedded/result/ в своем мобильном сафари на IOS и не прикасайтесь к устройству во время загрузки. Вы увидите список генерируемых отладочных сообщений, выглядящих следующим образом:

database is running
table will be cleared
store method called for '10'.
about to insert '10'.
transaction successful for '10'
store method called for '9'.
about to insert '9'.
transaction successful for '9'
store method called for '8'.
about to insert '8'.
transaction successful for '8'
[...]

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

database is running
table will be cleared
store method called for '10'.
about to insert '10'.
transaction successful for '10'
store method called for '9'.
about to insert '9'.
transaction successful for '9'
store method called for '8'.
transaction successful for '8' <-- WHERE IS MY "about to insert '8'." ???
store method called for '7'.
about to insert '7'.
transaction successful for '7'
[...]

Это потому, что транзакционная транзакция полностью пропущена! Но почему? И ПОЧЕМУ удастся избежать выстрела с помощью killCellback?

[Это упрощенный пример, пожалуйста, не говорите мне, чтобы я не делал этого setTimeout. В реальном мире есть данные, загружаемые async, а затем вставленные...:)]

Я думаю, что здесь есть аналогичная проблема транзакция HTML5 Web SQL Missing In Action, но нет никакого решения или намека.

Любые идеи? Я застрял... Спасибо!

Ответ 1

Наше тестирование показало, что это поведение также проявляется всякий раз, когда клавиатура показывает и предотвратит транзакции в событии onblur или onfocus (хотя не onkey {down | up | press}). Использование setTimeout может привести к серьезным проблемам с производительностью. Мы обнаружили, что обратный вызов успеха .transaction() будет запущен, даже если обратный вызов транзакции не был вызван, поэтому придумал этот метод, который хорошо работает для нас:

var oldOpenDatabase = window.openDatabase;
window.openDatabase = function() {
    var db = oldOpenDatabase.apply(window, arguments);

    var oldTrans = db.transaction;
    db.transaction = function(callback, err, suc) {
        var db = this;
        var params = arguments;
        var hasRun = false;
        oldTrans.call(db, function(tx){
            hasRun = true; callback(tx);
        }, err||function(){}, function(tx) {
            if(hasRun){
                if (suc != undefined)
                    suc(tx); 
                return;
            }
            else {
                oldTrans.apply(db, params);
            }
        });
    }

    return db;
}

Этот код проверяет, что обратный вызов транзакции был запущен в обработчике успеха и, если нет, дает ему еще один шанс.

Ответ 2

Работники Web и OpenDatabaseSync исправляют ту же проблему на моем сайте. Кроме того, веб-работники не поддерживаются на iOS4.x и Android, поэтому мы можем реализовать старый способ для тех и для веб-работников на iOS5.

В самом базовом примере ниже используется цикл, чтобы сделать одну вставку 500 раз, записывая на экран после каждой вставки. В конце делается запрос на возврат количества строк в таблице. Теперь мы видим, что транзакции не пропускаются даже при большой прокрутке.

Этот пример разработан, чтобы вы могли проверить устойчивость к прокрутке во время вставок на iPhone или iPad.

Требуются две страницы, html-страница и отдельный файл javascript для рабочего. Итак, для html-страницы;

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" name="viewport"/>
    <meta name="apple-mobile-web-app-capable" content="yes" />
    <meta name="apple-mobile-web-app-status-bar-style" content="black" />
    <title></title>
    <script src="../scripts/jquery-1.6.2.min.js" type="text/javascript"></script>
    <style type="text/css">
    body
    {
    background-color:Black;
    }
    .number
    {
    color:#00FF00;
    font-family:Courier New;
    font-size:12pt;
    }
    button
    {
    position:fixed;
    right:5px;
    top:5px;
    padding:15px;
    }
    </style>
    <script type="text/javascript">
    var worker;
    $(document).ready(function () {

        worker = new Worker('worker1.js');
        worker.addEventListener('message', function (e) {
        $("#test").html(new Date().toLocaleTimeString() + " " + e.data.message + "<BR/>" + $("#test").html());
        if (e.data.complete == true) {
            worker.terminate();
        }
        }, false);
    });

    function stop() {
        worker.terminate();
    }
    </script>

</head>
<body>
    <form id="form1" onsubmit="return false">
    <div id="test" class="number">
    <button onclick="stop()">Stop</button>
    </div>
    </form>
</body>
</html>

и worker1.js;

var wdb = {};
wdb.db = openDatabaseSync('test', '1.0', 'DB test', 1024);

doQuery("CREATE TABLE IF NOT EXISTS test(abc INTEGER PRIMARY KEY);");
doQuery("DELETE FROM test"); // optional

for (var i = 0; i < 500; i++) {
    doQuery("INSERT INTO test(abc) VALUES( " + i + " );");
    postMessage({ message: "Inserted " + i.toString(), complete: false });
}

var summary= doQuery("SELECT COUNT(*) AS Total FROM test;");
postMessage({ message: "Found " + summary.item(0).Total.toString() + " rows", complete: true });

function doQuery(sql) {
    wdb.db.transaction(function (tx) {
    rs = tx.executeSql(sql, []);
    });
    return rs.rows;
}