【问题标题】:console.log() async or sync?console.log() 异步还是同步?
【发布时间】:2017-06-22 02:04:45
【问题描述】:

我目前正在阅读 Trevor Burnham 的 Async Javascript。到目前为止,这是一本很棒的书。

他谈到这个 sn-p 和 console.log 在 Safari 和 Chrome 控制台中是“异步”的。不幸的是,我无法复制这一点。代码如下:

var obj = {}; 
console.log(obj); 
obj.foo = 'bar';
// my outcome: Object{}; 'bar';
// The book outcome: {foo:bar};

如果这是异步的,我预计结果将是书籍结果。 console.log() 被放入事件队列中,直到所有代码都被执行,然后它被运行并且它会有 bar 属性。

虽然它是同步运行的,但它似乎是。

我运行这段代码错了吗? console.log 实际上是异步的吗?

【问题讨论】:

  • @thefourtheye:不,所以我应该删除我的评论。
  • 我已经看到它发生在 Chrome 中。如果您 console.log 一个简单的对象,然后立即更改对象中的某些内容,console.log() 并不总是显示前一个值。如果发生这种情况,解决方法是将您尝试的任何内容转换为 console.log() 为不可变的字符串,因此不受此问题的影响。因此,根据经验console.log() 有一些异步问题可能与跨进程边界封送数据有关。这不是预期的行为,而是console.log() 在内部工作方式的一些副作用(我个人认为这是一个错误)。
  • @bergi 我只花了 10 分钟就找到了这个骗子(虽然我知道确切的名字),可能是因为它是封闭的。难道我们不能只交换副本,让另一个成为被骗者...?
  • @JonasWilms 我现在重新打开了这个问题(请参阅history)。我不认为它们是相互重复的,我使用 Is Chrome's JavaScript console lazy about evaluating arrays? 作为专门涉及数组的问题的规范目标。

标签: javascript asynchronous


【解决方案1】:

console.log 没有标准化,因此行为是相当不确定的,并且可以很容易地从开发工具的发布到发布进行更改。你的书可能已经过时了,我的回答也可能很快。

对于我们的代码,console.log 是否异步没有任何区别,它不提供任何类型的回调。并且您传递的值始终在您调用函数时被引用和计算。

我们真的不知道那时会发生什么(好吧,我们可以,因为 Firebug、Chrome Devtools 和 Opera Dragonfly 都是开源的)。控制台需要将记录的值存储在某处,并将它们显示在屏幕上。渲染肯定会异步发生(被限制为速率限制更新),未来与控制台中记录的对象的交互也会发生(例如扩展对象属性)。

因此,控制台可能会克隆(序列化)您记录的可变对象,或者存储对它们的引用。第一个不适用于深/大对象。此外,至少控制台中的初始渲染可能会显示对象的“当前”状态,即登录时的状态 - 在您的示例中,您会看到 Object {}

但是,当您展开对象以进一步检查其属性时,控制台可能只存储了对您的对象及其属性的引用,现在显示它们将显示其当前(已经变异)状态。如果您点击+,您应该能够在您的示例中看到bar 属性。

这是张贴在bug report 的屏幕截图,用于解释他们的“修复”:

因此,某些值可能在它们被记录很久之后才被引用,并且对这些值的评估相当惰性(“当需要时”)。这种差异最著名的例子是问题Is Chrome's JavaScript console lazy about evaluating arrays?

解决方法是确保始终记录对象的序列化快照,例如通过做console.log(JSON.stringify(obj))。不过,这仅适用于非圆形和相当小的物体。另见How can I change the default behavior of console.log in Safari?

更好的解决方案是使用断点进行调试,其中执行完全停止,您可以检查每个点的当前值。仅对可序列化和不可变数据使用日志记录。

【讨论】:

  • 我遇到了同样的问题,console.log 不是异步的。使用 JSON.stringify 为我修复了它
  • 截至 2019 年,我们可以说 console.log 在 Chrome 中仍然是异步的,因为它已经有 8 年历史了(参见 stackoverflow.com/questions/7389069/…),唯一改变的是现在 Chrome 输出的快照调用 console.log 时的引用对象(如果展开记录的对象,您将在 console.log 之后进行突变操作后看到其最终属性和值),还是 console.log 确实是同步的?
  • @tonix 是的,由于我的回答中列出的原因,这种行为不太可能改变。这不是错误,它只是交互式调试器/检查器的工作方式。
  • 如果你使用JSON.parse(JSON.stringify(obj)),正如评论here中提到的那样,你会得到一个对象形式的快照,而不是一个字符串。
  • TL;DR.way way TL
【解决方案2】:

这并不是问题的真正答案,但对于偶然发现这篇文章的人来说可能很方便,而且评论太长了:

window.console.logSync = (...args) => {
  try {
    args = args.map((arg) => JSON.parse(JSON.stringify(arg)));
    console.log(...args);
  } catch (error) {
    console.log('Error trying to console.logSync()', ...args);
  }
};

这将创建 console.log 的伪同步版本,但具有与已接受答案中提到的相同的警告。

由于目前大多数浏览器的console.log 似乎在某种程度上是异步的,因此您可能希望在某些情况下使用这样的函数。

【讨论】:

    【解决方案3】:

    使用 console.log 时:

    a = {}; a.a=1;console.log(a);a.b=function(){};
    // without b
    a = {}; a.a=1;a.a1=1;a.a2=1;a.a3=1;a.a4=1;a.a5=1;a.a6=1;a.a7=1;a.a8=1;console.log(a);a.b=function(){};
    // with b, maybe
    a = {}; a.a=function(){};console.log(a);a.b=function(){};
    // with b
    

    在第一种情况下,对象很简单,因此控制台可以将其“字符串化”然后呈现给您;但在其他情况下,a 太“复杂”而无法“字符串化”,因此控制台会改为显示内存中的对象,是的,当您查看它时,b 已经附加到 a。

    【讨论】:

    • 我知道这个问题已经 3 年了,但现在我遇到了同样的问题——序列化对象对我不起作用,因为它太复杂了。我正在捕捉一个试图访问它的数据的事件,但不知何故它在代码中没有数据,但在 console.log 中它有数据。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-01-17
    • 2020-05-15
    相关资源
    最近更新 更多