JavaScript异步回调生命周期

时间:2019-01-13 16:51:49

标签: javascript asynchronous websocket xmlhttprequest

我是javascript新手,但具有一定的c ++背景,因此以下内容使我感到困惑:我从浏览器创建了一些WebSocket连接,如下所示:

function ready() {
    var connection = new WebSocket('ws://localhost:3000');

    connection.onmessage = function(msg) {        
        console.log(msg.data);
    };
}

document.addEventListener("DOMContentLoaded", ready);

connection变量是局部变量,因此WebSocket退出时,对ready()对象的引用超出了范围。我的期望是该连接应随其上的回调一起消失,但是以某种方式我仍然能够获得connection.onmessage回调。我的理解是,它与javascript范围无关,而与WebSocket连接的内部组织方式有关。是这样吗?您能否解释这种行为。我相信javascript中的每个异步api都一样,

function get(url) {
    var req = new XMLHttpRequest();
    req.open("GET", url);

    req.onload = function() {
        console.log(req.response);
    }

    req.send();
}

get("file.json");

因此,似乎回调未绑定到放置它们的对象上。提前致谢!

2 个答案:

答案 0 :(得分:2)

JavaScript是一种垃圾收集语言。这意味着Javascript中变量的对象或内容在超出函数范围时不会自动释放。取而代之的是,对象一直存在,直到没有更多活动代码可以到达它为止。

因此,在函数中,您在connection对象上设置了事件处理程序。只要某处有活动代码仍然引用该连接对象,那么Javascript就不会垃圾收集该对象。

在这种特殊情况下,connection对象代表与另一台主机的实时TCP连接,并且该TCP连接后面有Javascript代码和本机代码,它们仍然引用该连接对象,并且仍然可以触发事件它。 Javascript引擎知道此代码仍然具有对该对象的引用,因此它不会垃圾收集它。而且,足够肯定的是,只要有新数据包到达webSocket连接,它就会调用您的onMessage处理程序。

使该连接对象有资格进行垃圾回收的方法是关闭连接。这将导致webSocket代码及其背后的本机代码清除对连接对象的所有引用,然后才有资格进行垃圾回收。

在Javascript中,永远不要认为函数局部变量的生存期与函数返回的时间严格相关。您很快就会发现(特别是由于Javascript中许多操作的异步特性),函数中的局部变量在函数返回后仍保持活跃状态​​,这非常有用。

我使用的思维模型(可能确切地说可能不是它的实现方式)可以帮助我理解所有这些知识,而永远不要将函数中的局部变量视为分配在堆栈框架上。相反,可以将它们视为在堆上动态分配。它们可以根据需要在该堆上保留很长时间,包括函数返回后的很长时间。当不再有代码使用它们或可以再使用它们时,垃圾收集器将释放它们。

由于异步回调(如您的示例中所示),这样的回调很容易导致局部变量在包含函数返回后很长一段时间保持活动状态。

  

连接变量是本地变量,因此当ready()退出时,对WebSocket对象的引用超出了范围

当包含函数返回时,函数作用域中的内容不会自动释放。它们没有像在C / C ++中那样分配在堆栈上。它们是垃圾收集的,当不再使用时将被释放。

  

我的期望是该连接应随其上的回调一起消失,但我仍然能够以某种方式获得连接。onmessage回调

参见上文。 Javascript中的局部变量可以在其宿主函数返回后存活很长时间。

  

我的理解是,它与javascript范围无关,而与WebSocket连接的内部组织方式有关。

这不是特定于webSocket的。如上所述,这是由于Javascript垃圾回收。管理(仍然有效)webSocket连接的代码具有对connection对象的实时引用,因此可以防止对其进行垃圾回收。要终止该实时引用,请关闭webSocket连接,然后管理webSocket连接的代码将清除其对连接对象的引用,然后才有资格进行垃圾回收。

答案 1 :(得分:0)

JavaScript中的事件侦听器更像是订阅。在您退订之前,您将继续收到信息。如果要在收到一条消息后关闭此连接,可以将onload函数设置为在获取信息后立即关闭连接:

function ready() {
    var connection = new WebSocket('ws://localhost:3000');

    connection.onmessage = function(msg) {        
        console.log(msg.data);
        // this closes the websocket connection
        connection.close();
    };
}

document.addEventListener("DOMContentLoaded", ready);

更新

通常,您关心的是JavaScript中闭包的工作方式。我无法直接链接到它,但是如果您转到here并向上滚动3个代码块,则会有一个有关回调和闭包的示例。它更多地涉及迭代,但是具有一些相关的信息。整篇文章绝对值得阅读,以更好地处理JavaScript范围中的某些细微差别。