console.log () асинхронно или синхронно?

93

В настоящее время я читаю 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 monster
1
Я видел это в Chrome. Если вы console.log простой объект, а затем немедленно измените что-то в объекте, console.log()не всегда будет отображаться прежнее значение. Решение, если это произойдет с вами, состоит в том, чтобы преобразовать все, что вы пытаетесь сделать, console.log()в строку, которая является неизменяемой, поэтому не подвержена этой проблеме. Итак, по опыту console.log()есть некоторые проблемы с асинхронностью, вероятно, связанные с маршалингом данных через границы процессов. Это не предполагаемое поведение, а некоторый побочный эффект console.log()внутренней работы (я лично считаю это ошибкой).
jfriend00
1
@bergi, мне потребовалось 10 минут, чтобы найти этот обман (хотя я знал точное имя), вероятно, потому, что он дублирован. Не могли бы мы просто поменять местами дубликат, чтобы другой оказался обманщиком ...?
Йонас Вильмс,
1
@JonasWilms Я снова открыл этот вопрос (см. Историю ). Я не думаю, что они дублируют друг друга, я использую Консоль JavaScript в Chrome ленив при оценке массивов? как каноническая цель для задач, конкретно связанных с массивом.
Берги

Ответы:

114

console.logне стандартизирован, поэтому поведение скорее неопределенное, и его можно легко изменить от выпуска к выпуску инструментов разработчика. Ваша книга, вероятно, устарела, как и мой ответ в ближайшее время.

Для нашего кода не имеет значения, console.logявляется ли он асинхронным или нет, он не обеспечивает какого-либо обратного вызова или чего-то подобного; и значения, которые вы передаете, всегда ссылаются и вычисляются во время вызова функции.

Мы действительно не знаем, что происходит потом (хорошо, мы могли бы, поскольку Firebug, Chrome Devtools и Opera Dragonfly имеют открытый исходный код). Консоль должна будет где-то хранить зарегистрированные значения, и она будет отображать их на экране. Рендеринг обязательно будет происходить асинхронно (дросселируется до обновлений ограничения скорости), как и будущие взаимодействия с зарегистрированными объектами в консоли (например, расширение свойств объекта).

Таким образом, консоль может либо клонировать (сериализовать) изменяемые объекты, которые вы записали в журнал, либо сохранять ссылки на них. Первый не работает с глубокими / большими объектами. Кроме того, по крайней мере, первоначальный рендеринг в консоли, вероятно, покажет «текущее» состояние объекта, то есть то, когда он был зарегистрирован - в вашем примере вы видите Object {}.

Однако, когда вы расширяете объект для дальнейшего изучения его свойств, вполне вероятно, что консоль сохранит только ссылку на ваш объект и его свойства, и при их отображении будет показано их текущее (уже измененное) состояние. Если вы нажмете на +, вы должны увидеть barсвойство в своем примере.

Вот скриншот, который был опубликован в отчете об ошибке, чтобы объяснить их «исправление»:

Таким образом, на некоторые значения можно ссылаться спустя долгое время после того, как они были зарегистрированы, и их оценка довольно ленивая («при ​​необходимости»). Самый известный пример этого несоответствия рассматривается в вопросе "Ленится ли консоль JavaScript Chrome оценивать массивы?"

Обходной путь - всегда записывать сериализованные снимки ваших объектов, например, путем выполнения console.log(JSON.stringify(obj)). Однако это будет работать только для некруглых и довольно маленьких объектов. См. Также Как изменить поведение console.log по умолчанию в Safari? .

Лучшее решение - использовать точки останова для отладки, когда выполнение полностью останавливается, и вы можете проверить текущие значения в каждой точке. Используйте ведение журнала только для сериализуемых и неизменяемых данных.

Берги
источник
2
У меня была такая же проблема с console.log, который не был асинхронным. используя JSON.stringify фиксированной для меня
russiansummer
Можем ли мы сказать, что по состоянию на 2019 год console.logв Chrome все еще асинхронно, поскольку ему было 8 лет (см. Stackoverflow.com/questions/7389069/… ), единственное, что изменилось, это то, что теперь Chrome выводит снимок эталонного объекта в во время вызова console.log(если вы развернете зарегистрированный объект, вы увидите его окончательные свойства и значения после операций мутации, которые вы сделали после console.log), или console.logдействительно синхронно?
tonix
@tonix Да, это поведение вряд ли будет изменено по причинам, изложенным в моем ответе. Это не ошибка, это просто принцип работы интерактивного отладчика / инспектора.
Берги
Если вы используете, JSON.parse(JSON.stringify(obj))как также упомянуто в комментарии здесь, вы получите снимок в форме объекта вместо строки.
Уилт,
TL; DR. Путь путь TL
Рик О'Ши
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