飞镖中的异步编程

时间:2012-11-22 07:17:25

标签: asynchronous dart

我与java有关如何做线程/异步。我使用新的Thread(target).start(),其中target是Runnable,作为在java中进行线程化的一种方法。新的并发api有其他选择但我们知道在特定的调用中新的线程正在创建并传递任务被执行。

同样如何在Dart中完成异步? 我读了发送/接收,完成/未来,spawnFunction。对我来说,只有spawnFunction是令人信服的声明,它将创建新的线程。可以解释完成/未来的帮助。我知道他们采取回调,但javascript / dart中有一些隐含的逻辑/规则,回调总是在不同的线程中执行。

下面的

是dart片段/伪代码:

void callback() {
  print("callback called");
}

costlyQuery(sql, void f()) {
  executeSql(sql);
  f();
}

costlyQuery("select * from dual", callback);

我希望我的昂贵的Query签名将函数作为第二个参数是正确的。所以现在我不认为f()之后的executeSql(sql)将是异步的。可能正在上面的例子中添加完成者/将来如果可以使异步帮助我理解。

1 个答案:

答案 0 :(得分:9)

tl; dr :没有隐含的规则,回调不会阻止。

Javascript事件队列

在Javascript中,没有线程(WebWorkers除外,但这有所不同)。这意味着如果代码的任何部分阻塞,整个应用程序将被阻止。回调没有什么神奇之处,它们只是功能:

function longLoop(cb) {
    var i = 1000000;
    while (i--) ;
    cb();
}

function callback() {
    console.log("Hello world");
}

function fun() {
    longLoop(callback);
    console.log("Called after Hello World is printed");
}

要使异步生成,必须将回调放在事件队列中,这只能通过某些API调用发生:

  • 用户启动的事件处理程序 - 点击,键盘,鼠标
  • API事件处理程序 - XmlHTTPRequest回调,WebWorker通信
  • 计时功能 - setTimeout,setInterval

当事件被触发时,回调被放置在事件队列上,以便在所有其他回调完成执行时执行。这意味着不会有两行你的代码同时执行。

期货

我认为你至少偶然发现this post about futures。如果没有,这是一个很好的阅读,直接来自马口。

在Javascript中,你必须做一些工作来理解异步代码。甚至还有一个名为futures的Javascript库用于执行常见操作,例如异步循环和序列(完全公开,我个人与该库的作者合作过)。

未来的概念是创造未来的功能所做出的承诺,未来将在未来的某个时刻完成。如果要进行异步调用,请创建未来,返回它,并在异步调用完成时履行承诺。来自博客文章:

Future<Results> costlyQuery() {
    var completer = new Completer();

    database.query("SELECT * FROM giant_table", (results) {
        // when complete
        completer.complete(results);
    });

    // this returns essentially immediately,
    // before query is finished
    return completer.future; 
}

如果database.query是阻止调用,则将立即完成将来的操作。此示例假定database.query是非阻塞调用(异步),因此将在此函数退出后继续执行。 completer.complete将使用指定的参数调用传递给completer.then()的任何函数。

您的示例,修改为惯用和异步:

void callback() {
  print("callback called");
}

costlyQuery(sql) {
  var completer = new Completer();
  executeSql(sql, () => completer.complete());
  return completer.future;
}

costlyQuery("select * from dual").then(callback);

这是异步的并正确使用期货。您可以将自己的回调函数传递给costlyQuery,并在回调executeSql中调用它来实现相同的功能,但这是Javascript方式,而不是Dart方式。

分离株

隔离类似于Java的线程,除了隔离是并发模型,线程是并行模型(有关并发性和并行性的更多信息,请参阅this SO question。)

Dart的特殊之处在于规范并未强制要求所有内容都在单个线程中运行。它只强制要求不同的执行上下文不能访问相同的数据。每个隔离区都有自己的内存,只能通过发送/接收端口与其他隔离区进行通信(例如发送数据)。

如果您熟悉,这些端口与Go中的频道类似。

隔离是一个独立于其他代码运行的独立执行上下文。在Javascript术语中,这意味着它拥有自己的事件队列。有关分离株的更完整介绍,请参阅the Dart tour

让我们说你的executeSql是一个阻塞函数。如果我们不想等待它完成,我们可以将它加载到隔离区中:

void callback() {
    print("callback called");
}

void costlyQuery() {
    port.receive((sql, reply) {
        executeSql(sql);
        reply.send();
    });
}

main() {
    var sendPort = spawnFunction(costlyQuery);

    // .call does all the magic needed to communicate both directions
    sendPort.call("select * from dual").then(callback);

    print("Called before executeSql finishes");
}

此代码创建隔离,向其发送数据,然后在完成后注册回调。即使executeSql阻止,main()也不一定会阻止。