将异步 API 转换为同步 API。 (我有充分的理由保证)

Posted

技术标签:

【中文标题】将异步 API 转换为同步 API。 (我有充分的理由保证)【英文标题】:converting an asynchronous api to synchronous one. (I have a good reason I promise) 【发布时间】:2013-11-15 19:02:58 【问题描述】:

所以我们有一个现有的、相当大的应用程序,它使用了一个同步的 javascript API。在这种情况下,Google Gears SQL。我们正在尝试为我们的客户演示,可以让应用程序在不支持齿轮的平台(在本例中为 ios)上运行,如果他们喜欢他们所看到的,我们将使用 html5 以正确的方式重做整个应用程序localStorage 但现在我们需要使用 Web SQL 让它工作,即使效果不佳。问题当然是 Web SQL 是异步的,而 Gears SQL 是同步的。我们已经有了一个用于与齿轮对话的抽象,因此如果我们想更改该抽象以与 Web SQL 对话。我尝试使用类似这样的方法:http://jsfiddle.net/ZCD4u/ 来伪造同步行为,但 Web SQL 查询在被阻止时从未执行过。我还尝试将所有 db 内容放在 web worker 中,认为它会阻塞页面但无论如何都会执行 SQL。问题是,当我的睡眠循环仍然阻塞时,等待从工作人员那里收到回复的侦听器永远不会被解雇。我需要的是一种修改使用同步 api 的抽象的方法,以便它使用异步 api 而不更改我自己的抽象的 api。也就是说,如果当我完成抽象时,它会向应用程序的其余部分公开一个回调机制,那么我就失败了。更具体地说:

我不允许更改的文件:

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

我需要更改的文件,以便它可以使用 Web SQL 而不是 Gears:

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

由于sleep 在 Javascript 中不是真实存在而无法使用的解决方案:

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

任何改变上述第一个代码块中代码的行为或内容的解决方案都会失败。

编辑:我怀疑这无法完成,我们正在研究此演示的其他选项,但我很想知道是否有人有解决方案。

【问题讨论】:

我不确定它是否直接满足您的需求,但我前几天偶然发现了 jQuery BlockUI 插件:malsup.com/jquery/block Call An Asynchronous Javascript Function Synchronously的可能重复 今天早上我会说这是不可能的。然后我偶然发现了这个聪明、聪明但邪恶的 hack。请记住,强大的力量伴随着重大的责任。明智地使用它:***.com/questions/8448218/… @MikeEdwards 这确实是一个很棒的 hack。有趣的是,这个特定用例的全部意义在于我们需要它离线运行,所以这个 hack 对我们不起作用。 你可能不走运。问题是JS是单线程的。当你调用一个异步 API 时,它不能得到它的结果,直到解释器返回到主事件循环,直到你的函数返回之后。 【参考方案1】:

WebSQL 是异步 API。无法将异步函数转换为同步函数,因为它依赖于运行到完成的执行模型。这意味着回调函数只有在当前执行堆栈完成后才会调用。

但是,新的 ecmascript 标准中有 generator,它允许暂停执行上下文。执行是在每个 yield 语句上暂停。您可以在数据库请求异步时使用该功能编写线性工作流,如下所示:

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'));

您可以在 Firefox nightly 和 Chrome 上测试 this unit test,并打开和谐标志。

【讨论】:

谢谢,很遗憾,这并不能解决问题,因为我们需要它在不支持 yield 的地方工作。

以上是关于将异步 API 转换为同步 API。 (我有充分的理由保证)的主要内容,如果未能解决你的问题,请参考以下文章

将同步 API 包装到 Async 方法中

将 jquery .getJSON 转换为 ajax 异步等待 YouTube API

RPC-非阻塞通信下的同步API实现原理,以Dubbo为例

web api接口同步和异步的问题

如何在异步协程中包装同步函数?

同步 API 请求到异步 API 请求 Swift 2.2