console.log()asyncまたはsync?


93

私は現在、TrevorBurnhamによるAsyncJavascriptを読んでいます。これはこれまでのところ素晴らしい本です。

彼は、このスニペットと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:いいえ、コメントを削除する必要があります。
クッキーモンスター

1
私はそれがChromeで起こるのを見ました。単純なオブジェクトをconsole.logに記録し、すぐにオブジェクト内の何かを変更した場合、はconsole.log()常に前の値を表示するとは限りません。これが発生した場合の回避console.log()策は、この問題の影響を受けないように不変の文字列に変換しようとしているものを変換することです。したがって、経験から、console.log()おそらくプロセスの境界を越えたデータのマーシャリングに関連するいくつかの非同期の問題があります。これは意図された動作ではありませんが、console.log()内部でどのように機能するかという副作用です(個人的にはバグだと思います)。
jfriend00 2014


1
@bergiこの重複を見つけるのに10分かかりました(正確な名前は知っていましたが)。おそらく重複しているためです。複製を交換して、もう一方が複製になるようにすることはできませんでした...?
JonasWilms19年

1
@JonasWilmsこの質問を再開しました(履歴を参照)。それらが互いに重複しているとは思わない、私は使用しているChromeのJavaScriptコンソールは配列の評価について怠惰ですか?特にアレイに関連する問題の標準的なターゲットとして。
ベルギ

回答:


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 2017年

2019年の時点で、console.log8年前のChromeではまだ非同期であると言えますか(stackoverflow.com/questions/7389069/…を参照)、変更されるのは、Chromeが参照オブジェクトのスナップショットを出力することだけです。呼び出すときconsole.log(ログに記録されたオブジェクトを展開すると、後で行ったミューテーション操作の後にその最終的なプロパティと値が表示されますconsole.log)、またはconsole.log実際に同期していますか?
tonix

@tonixはい、私の回答に記載されている理由により、この動作が変更される可能性はほとんどありません。これはバグではなく、インタラクティブなデバッガー/インスペクターの動作方法です。
ベルギ

ここ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 2017年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.