console.log()异步还是同步?


93

我目前正在阅读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实际上是异步的吗?


@thefourtheye:不,所以我应该删除我的评论。
cookie怪物

1
我已经看到它在Chrome中发生。如果您console.log一个简单的对象,然后立即更改该对象中的某些内容,则console.log()并不总是显示前一个值。如果发生这种情况,解决方法是将您要尝试的任何内容都转换console.log()为不可变的字符串,因此不会出现此问题。因此,从经验来看,console.log()可能存在一些异步问题,这些问题可能与跨流程边界封送数据有关。这不是预期的行为,而是console.log()内部工作方式的一些副作用(我个人认为这是一个错误)。
jfriend00 2014年


1
@bergi,我花了10分钟才找到这个骗子(尽管我知道确切的名字),可能是因为它被骗了。我们不能只交换副本,让另一个成为重复对象吗?
乔纳斯·威尔姆斯

1
@JonasWilms我现在重新打开了这个问题(请参阅历史记录)。我不认为它们是彼此重复的,我使用Chrome的JavaScript控制台是否懒于评估数组?作为专门涉及数组的问题的规范目标。
Bergi

Answers:


114

console.log未标准化,因此行为相当不确定,可以在开发人员工具的发行之间轻松更改。您的书很可能已经过时,很快我的回答也可能会过时。

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

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

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

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

这是在错误报告中发布的屏幕截图,用于解释其“修复”:

因此,某些值可能在记录之后很久才被引用,并且对它们的评估相当懒惰(“何时需要”)。这个差异最著名的例子是Chrome的JavaScript控制台是否懒于评估数组?

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

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


2
我在console.log没有同步时遇到了同样的问题。透过JSON.stringify固定对我来说
russiansummer

截至2019年,我们可以说console.log在8年前的Chrome中它仍然是异步的(请参阅stackoverflow.com/questions/7389069/…),唯一改变的是现在Chrome在以下位置输出参考对象的快照:调用的时间console.log(如果您扩展了记录的对象,则在执行之后的突变操作之后,您将看到其最终属性和值console.log),或者console.log确实是同步的?
tonix

@tonix是的,由于我的回答中列出的原因,这种行为不太可能改变。这不是错误,而只是交互式调试器/检查器的工作方式。
Bergi

如果您使用的JSON.parse(JSON.stringify(obj))是同样在评论中提及了在这里你得到的,而不是一个字符串对象形式的快照。
威尔特

TL; DR.way way way TL
Rick O'Shea

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在某种程度上都是异步的,因此您可能希望在某些情况下使用类似的功能。


0

使用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中它有数据。
Skeec
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.