HTML5 Web SQL Transactions skipped without error when touch triggered in IOS

I’m experiencing problems making database transactions on IOS devices.
If the user doesn’t touch the phone, everything works like expected.
If the user taps/scrolls/touches the screen, some transactions directly call their successCallback, without ever calling the actual transaction callback.

Simplified example here: http://jsfiddle.net/Tk9rv/

  • What is the best way to build a complex NSCompoundPredicate?
  • Delete multiple tables from a single query by separating from semicolon
  • Xcode sort array by specific (NSString) dictionary key (like sql query)
  • Best way to get the ID of the last inserted row on SQLite
  • Facebook connect Batch requests and FQL error problem
  • Why does Microsoft Azure (or Swift in general) fail to update a variable to return after a table query?
  • To test, just open http://jsfiddle.net/Tk9rv/embedded/result/ in your mobile safari on IOS and do not touch the device while loading. You will see a list of debug messages being generated looking like this:

    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'
    [...]
    

    Now, reload the page and while loading, scroll and tap randomly.
    You will see some “about to insert…” messages are missing.

    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'
    [...]
    

    This is because the transactionCallback is completely skipped! But WHY? And WHY does the successCallback fire instead of the errorCallback?

    [This is a simplified example, please do not tell me not to do this setTimeout stuff. In the real world, there is data being loaded async and then being inserted… 🙂 ]

    I think there is a similar problem here HTML5 Web SQL transaction Missing In Action but there is no solution or hint either.

    Any ideas? I’m stuck… Thanks!

    2 Solutions Collect From Internet About “HTML5 Web SQL Transactions skipped without error when touch triggered in IOS”

    Our testing showed that this behaviour would also occur whenever the keyboard was displaying and would prevent transactions in an onblur or onfocus event (although, not onkey{down|up|press}). Using setTimeout would cause terrible performance issues. We had found that the .transaction() success callback would be triggered, even if the transaction callback was not called, so came up with this method that works well for us:

    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;
    }
    

    This code checks that the transaction callback has fired in the success handler and, if not, gives it another chance.

    Web Workers and OpenDatabaseSync fix the same problem on my site. Except web workers are not supported on iOS4.x and Android so we can implement the old way for those and web workers on iOS5.

    The very basic example below uses a loop to do a single insert 500 times writing to the screen after each insert. At the end a query is made to return the number of rows in the table. Now we can see transactions are not skipped even with a lot of scrolling.

    This example is designed so you can test the resilience to scrolling during the inserts on iPhone or iPad.

    Two pages are required, an html page and separate javascript file for the worker. So for the html page;

    <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>
    

    and 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;
    }