console.log () async ou sync?

93

Atualmente, estou lendo Async Javascript de Trevor Burnham. Este tem sido um grande livro até agora.

Ele fala sobre esse snippet e o console.log serem 'assíncronos' no console Safari e Chrome. Infelizmente, não posso replicar isso. Aqui está o código:

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

Se isso fosse assíncrono, eu anteciparia que o resultado seria o resultado dos livros. console.log () é colocado na fila de eventos até que todo o código seja executado, então ele é executado e teria a propriedade bar.

Parece que está sendo executado de forma síncrona.

Estou executando este código errado? O console.log é realmente assíncrono?

Olá Mundo
fonte
@thefourtheye: Não, então eu provavelmente deveria apenas deletar meu comentário.
Cookie Monster de
1
Eu vi isso acontecer no Chrome. Se você console.log um objeto simples e, em seguida, altere imediatamente algo no objeto, console.log()nem sempre mostra o valor anterior. A solução, se isso acontecer com você, é converter tudo o que você está tentando console.log()em uma string imutável, portanto, não está sujeita a esse problema. Portanto, por experiência própria, console.log()tem alguns problemas assíncronos provavelmente relacionados ao empacotamento de dados entre os limites do processo. Este não é o comportamento pretendido, mas é algum efeito colateral de como console.log()funciona internamente (eu pessoalmente considero um bug).
jfriend00
1
@bergi demorei apenas 10 minutos para encontrar esse ladrão (embora eu soubesse o nome exato), provavelmente porque ele está duplicado. Não poderíamos simplesmente trocar a duplicata, para que a outra seja a duplicata ...?
Jonas Wilms
1
@JonasWilms Agora reabri esta questão (veja a história ). Eu não acho que eles são duplicatas um do outro, eu uso O console JavaScript do Chrome é preguiçoso para avaliar matrizes? como o alvo canônico para problemas envolvendo especificamente um array.
Bergi de

Respostas:

114

console.lognão é padronizado, portanto, o comportamento é bastante indefinido e pode ser alterado facilmente de uma versão para outra das ferramentas de desenvolvedor. É provável que seu livro esteja desatualizado, assim como minha resposta em breve.

Para o nosso código, não faz diferença se console.logé assíncrono ou não, não fornece nenhum tipo de retorno de chamada ou algo assim; e os valores que você passa são sempre referenciados e calculados no momento em que você chama a função.

Não sabemos realmente o que acontece então (OK, poderíamos, já que Firebug, Chrome Devtools e Opera Dragonfly são todos de código aberto). O console precisará armazenar os valores registrados em algum lugar e os exibirá na tela. A renderização acontecerá de forma assíncrona com certeza (sendo estrangulada para atualizações de limite de taxa), assim como as futuras interações com os objetos registrados no console (como propriedades de objeto em expansão).

Portanto, o console pode clonar (serializar) os objetos mutáveis ​​que você registrou ou armazenar referências a eles. O primeiro não funciona bem com objetos grandes / profundos. Além disso, pelo menos a renderização inicial no console provavelmente mostrará o estado "atual" do objeto, ou seja, aquele quando ele foi logado - no seu exemplo, você vê Object {}.

No entanto, quando você expande o objeto para inspecionar ainda mais suas propriedades, é provável que o console tenha armazenado apenas uma referência ao seu objeto e suas propriedades, e exibi-los agora mostrará seu estado atual (já alterado). Se você clicar em +, deverá ver a barpropriedade em seu exemplo.

Aqui está uma captura de tela que foi postada no relatório de bug para explicar sua "correção":

Portanto, alguns valores podem ser referenciados muito tempo depois de terem sido registrados, e a avaliação deles é bastante lenta ("quando necessário"). O exemplo mais famoso dessa discrepância é tratado na pergunta O console JavaScript do Chrome tem preguiça de avaliar arrays?

Uma solução alternativa é certificar-se de registrar instantâneos serializados de seus objetos sempre, por exemplo, fazendo console.log(JSON.stringify(obj)). No entanto, isso funcionará apenas para objetos não circulares e pequenos. Veja também Como posso alterar o comportamento padrão do console.log no Safari? .

A melhor solução é usar pontos de interrupção para depuração, onde a execução para completamente e você pode inspecionar os valores atuais em cada ponto. Use o registro apenas com dados serializáveis ​​e imutáveis.

Bergi
fonte
2
Eu tive o mesmo problema com o console.log não ser assíncrono. usando JSON.stringify corrigiu para mim
russiansummer
Em 2019, podemos dizer que console.logainda é assíncrono no Chrome, pois tinha 8 anos (consulte stackoverflow.com/questions/7389069/… ), a única coisa que muda é que agora o Chrome emite um instantâneo do objeto de referência no vez que você chama console.log(se você expandir o objeto logado, verá suas propriedades e valores finais após as operações de mutação feitas depois console.log), ou é console.logrealmente síncrono?
tonix
@tonix Sim, é improvável que esse comportamento seja alterado devido aos motivos apresentados em minha resposta. Não é um bug, é apenas como funciona um depurador / inspetor interativo.
Bergi
Se você usar JSON.parse(JSON.stringify(obj))como também mencionado no comentário aqui, você obterá um instantâneo na forma de objeto, em vez de uma string.
Wilt
TL; DR.way way way TL
Rick O'Shea
2

Esta não é realmente uma resposta para a pergunta, mas pode ser útil para alguém que tropeçou nesta postagem e era muito longo para colocar um comentário:

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);
  }
};

Isso cria uma versão pseudo-síncrona de console.log, mas com as mesmas ressalvas mencionadas na resposta aceita.

Uma vez que parece que, no momento, a maioria dos navegadores console.logsão assíncronos de alguma maneira, você pode querer usar uma função como esta em certos cenários.

V. Rubinetti
fonte
0

Ao usar 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

na primeira situação, o objeto é simples o suficiente, então o console pode 'stringificar' e então apresentá-lo a você; mas nas outras situações, a é muito 'complicado' para 'stringify', então o console mostrará o objeto na memória, e sim, quando você olhar para ele, b já estará anexado a a.

Miao Siyu
fonte
Eu sei que esta pergunta tem 3 anos, mas agora estou com o mesmo problema - serializar o objeto não funciona para mim porque é muito complicado. Estou capturando um evento tentando acessar seus dados, mas de alguma forma ele não possui dados no código, mas em console.log ele possui dados.
Skeec de