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

IT技术 javascript asynchronous
2020-12-17 15:55:54

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

3个回答

console.log没有标准化,所以行为是相当未定义的,并且可以很容易地随开发人员工具的发布而改变。你的书可能已经过时了,我的回答也可能很快过时。

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

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

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

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

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

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

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

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

TL;DR.way way way TL
2021-02-21 15:55:54
到 2019 年,我们console.log是否可以说它在 Chrome 中仍然是异步的,因为它已经有 8 年的历史了(参见stackoverflow.com/questions/7389069/...),唯一改变的是现在 Chrome 在您调用的时间console.log(如果您展开记录的对象,您将在 之后进行变异操作后看到它的最终属性和值console.log),或者console.log确实是同步的?
2021-02-24 15:55:54
如果您使用这里JSON.parse(JSON.stringify(obj))的评论中也提到的那样,您将获得对象形式的快照,而不是字符串。
2021-03-07 15:55:54
我有同样的问题,console.log 不是异步的。使用 JSON.stringify 为我修复了它
2021-03-08 15:55:54
@tonix 是的,由于我的回答中列出的原因,这种行为不太可能改变。这不是错误,而是交互式调试器/检查器的工作方式。
2021-03-09 15:55:54

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

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以某种方式是异步的,因此您可能希望在某些情况下使用这样的函数。

使用 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 中有数据。
2021-03-09 15:55:54