控制台.log()异步还是同步?

console.log() async or sync?

提问人:HelloWorld 提问时间:4/30/2014 更新时间:12/13/2019 访问量:67235

问:

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

他谈到这个片段和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 实际上是异步的吗?

JavaScript 异步

评论

0赞 cookie monster 4/30/2014
@thefourtheye:不,所以我可能应该删除我的评论。
4赞 jfriend00 4/30/2014
我已经看到它发生在 Chrome 中。如果控制台.log一个简单的对象,然后立即更改对象中的某些内容,则并不总是显示以前的值。如果发生这种情况,解决方法是将您尝试的任何内容转换为不可变的字符串,因此不受此问题的影响。因此,根据经验,存在一些异步问题,可能与跨进程边界封送数据有关。这不是预期的行为,而是内部工作方式的一些副作用(我个人认为这是一个错误)。console.log()console.log()console.log()console.log()
0赞 Bergi 9/13/2015
另请参阅 console.log() 在变量实际更改之前显示变量的更改值
1赞 Jonas Wilms 4/15/2019
@bergi我只花了 10 分钟就找到了这个骗子(虽然我知道确切的名字),可能是因为它被骗了。难道我们不能把副本换掉,这样另一个就会被骗......?
1赞 Bergi 4/15/2019
@JonasWilms 我现在已经重新打开了这个问题(见历史)。我不认为它们是彼此的重复,我使用 Chrome 的 JavaScript 控制台在评估数组方面是否懒惰? 作为专门涉及数组的问题的规范目标。

答:

156赞 Bergi 4/30/2014 #1

console.log不是标准化的,因此行为是相当不确定的,并且可以很容易地从一个版本到另一个版本的开发人员工具进行更改。你的书可能已经过时了,我的答案可能很快就会过时了。

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

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

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

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

以下是发布在错误报告中的屏幕截图,以解释他们的“修复”:

因此,某些值可能会在记录后很久才被引用,并且对这些值的评估相当惰(“需要时”)。这种差异最著名的例子是在问题中处理的:Chrome 的 JavaScript 控制台在计算数组方面是否懒惰?

解决方法是确保始终记录对象的序列化快照,例如通过执行 .不过,这仅适用于非圆形和相当小的物体。另请参阅如何在 Safari 中更改控制台.log的默认行为?console.log(JSON.stringify(obj))

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

评论

2赞 russiansummer 11/16/2017
我在控制台.log不异步时遇到了同样的问题。使用JSON.stringify为我修复了它
0赞 tonix 11/3/2019
截至 2019 年,我们可以说它在 Chrome 中仍然是异步的,因为它已经有 8 年的历史了(参见 stackoverflow.com/questions/7389069/...),唯一改变的是现在 Chrome 会在您调用时输出引用对象的快照(如果您展开记录的对象,您将在您之后进行的突变操作后看到它的最终属性和值), 还是确实是同步的?console.logconsole.logconsole.logconsole.log
4赞 Wilt 3/2/2020
如果您使用此处注释中提到的,您将获得对象形式的快照,而不是字符串。JSON.parse(JSON.stringify(obj))
1赞 the Hutt 2/26/2022
@Wilt JSON.stringify 不会序列化函数、符号和 undefined。因此,您可能会在调试时丢失关键信息。
1赞 Inigo 3/8/2022
我可以确认,在截至今天最新版本的 Node(2022 年 3 月的 v17.6.0)中,console.log 是异步的。我把 和 语句放在根脚本和函数内部的旁边。首先打印所有 s,最后打印所有 s,不显示调用时打印对象的状态。所以我用console.logprocess.stdout.writeprocess.std.writeconsole.logprocess.stdout.write(`${JSON.stringify(object, null, 2)}\n`)
1赞 Miao Siyu 9/30/2014 #2

使用控制台.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。

评论

1赞 Skeec 6/14/2017
我知道这个问题已经有 3 年的历史了,但现在我遇到了同样的问题 - 序列化对象对我不起作用,因为它太复杂了。我正在捕获一个试图访问其数据的事件,但不知何故,它在代码中没有数据,但在控制台 .log 中它有数据。
3赞 V. Rubinetti 12/13/2019 #3

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

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

评论

1赞 the Hutt 2/26/2022
JSON.stringify不序列化函数、符号和未定义。因此,这种方法会丢失一些信息。