1

So we have an existing, fairly large, application that is using a synchronous javascript API. In this case Google Gears SQL. We are trying to demo for our clients that it is possible to get the app to work on platforms that don't support gears (in this case iOS) and if they like what they see we will redo the whole app the right way using HTML5 localStorage but for now we need to get it working, even if poorly, using Web SQL. The problem of course is that Web SQL is asynchronous and Gears SQL is synchronous. We already have an abstraction we are using to talk to gears so if we want to change that abstraction to talk to web SQL instead. I tried using an approach like this one: http://jsfiddle.net/ZCD4u/ to fake the synchronous behavior but the Web SQL query never executed while it was blocked. I also tried putting all of the db stuff in a web worker thinking that it would block in the page but execute the SQL anyway. The problem is that the listener that is waiting to hear back from the worker is never fired while my sleep loop is still blocking. What I need is a way to modify an abstraction that uses a synchronous api so that it instead uses an asynchronous api without changing the api of my own abstraction. That is to say that if when I'm done with the abstraction it exposes a callback mechanism to the rest of the app then I have failed. More concretely:

File that I am not allowed to change:

var sql = 'SELECT things FROM tables';
var res = myCoolAbstraction(sql);
dothings(res);

File that I need to change so that it can use Web SQL instead of Gears:

var myCoolAbstraction = function(sql) {
    return doGearsThing(sql);
};

Solution that doesn't work because sleep isn't a real thing in Javascript:

var myCoolAbstraction = function(sql) {
    var res;
    doWebSQLThing(sql, function(d) {
        res = d;
    });
    while (res === undefined) {
        sleep(100);
    }
    return res
};

Any solution that changes the behavior or content of the code in the first code block above fails.

Edit: I suspect this cannot be done and we are looking into other options for this demo but I would love to know if somebody has a solution.

olleicua
  • 2,039
  • 2
  • 21
  • 33
  • 1
    I'm not sure if it directly addresses your need, but I stumbled on the jQuery BlockUI plugin a few days ago: http://www.malsup.com/jquery/block/ – Barmar Nov 05 '13 at 00:37
  • possible duplicate of [Call An Asynchronous Javascript Function Synchronously](http://stackoverflow.com/questions/9121902/call-an-asynchronous-javascript-function-synchronously) – Barmar Nov 05 '13 at 00:40
  • 2
    This morning I would have said this is impossible. Then I stumbled over this clever, clever but _evil_ hack. Remember, with great power comes _great responsibility_. Use it wisely: http://stackoverflow.com/questions/8448218/how-to-call-an-asychronous-javascript-function-and-block-the-original-caller#answer-8453862 – Mike Edwards Nov 05 '13 at 00:40
  • @MikeEdwards That is indeed a fantastic hack. Hilariously the whole point of this particular use case is that we need it to run offline so this hack wont work for us. – olleicua Nov 06 '13 at 14:44
  • @Barmar the answer there will not work because it relies making the call to the currently synchronous api asynchronous. Also blockUI just prevents the user from doing anything it does nothing to stop the current function from returning which is the actual need. – olleicua Nov 06 '13 at 14:47
  • 1
    You may be out of luck. The problem is that JS is single-threaded. When you call an async API, it can't get its results until the interpreter returns to the main event loop, which isn't until after your function returns. – Barmar Nov 06 '13 at 15:02

1 Answers1

1

WebSQL is async API. It is not possible to convert async function to sync function since it rely on run-to-completion execution model. It means that callback function will invoke only after currently execution stack is completed.

However there is generator in new ecmascript standard, which allow suspended execution contexts. Execution is pause on each yield statement. You can use that feature to write linear workflow while database request are in async, as follow:

var db = new ydn.db.Storage(db_name, schema);
db.spawn(function* (tdb) {
  var value_1 = yield tdb.get('st', key_1);
  value_1.amount += 10;
  var key_1 = yield tdb.put('st', value_1);
  var value = yield tdb.get('st', key_1);
  console.log(value);
};, ['st'], 'readwrite'));

You can test this unit test on Firefox nightly and Chrome with harmony flag on.

Kyaw Tun
  • 12,447
  • 10
  • 56
  • 83
  • Thanks, unfortunately this won't solve the problem because we need it to work in places that don't support yield. – olleicua Nov 06 '13 at 14:40