Pull to refresh

Comments 35

Коллбэки в JavaScript для дошкольников? Подобные статьи появляются с определённой периодичностью. Но кто-то хоть не просто выдаёт материал 15-летней давности, а про промисы и async пишут. Хотя и по ним уже уйма статей есть.

как преобразовать callback:


function doHomework(subject, callback) {
  alert(`Starting my ${subject} homework.`);
  callback();
  alert('Stop');
}
function alertFinished(){
  alert('Finished my homework');
}
doHomework('math', alertFinished);

к промисам:


function doHomework(subject) {
  return new Promise(function(resolve) {
    alert(`Starting my ${subject} homework.`);
    resolve();
    alert('Stop');
  });
}
doHomework('math').then(alert('Finished my homework'));

ожидаю выполнение alert('Stop') после alert('Finished my homework')


почему работает наоборот?

Потому что продолжения всегда исполняются асинхронно. Формальное требование для A+/Promises звучит примерно так: "при исполнении продолжения в стеке вызовов не должно быть никаких фреймов кроме инфраструктурных".

вот как можно обойти:


function doHomework(subject) {
    return new Promise(async function (resolve) {
        alert(`Starting my ${subject} homework.`);
        await resolve();
        alert('Stop');
    });
}
doHomework('math').then(alert('Finished my homework'));

В том, что вы написали — нет смысла. resolve(); ничего не возвращает, а применять к нему await бессмысленно. Аналогично бессмысленно делать функцию, переданную в конструктор Promise, асинхронной — никто не будет проверять что она вернула.


Возможно, вам показалось что все работает как вы задумывали из-за нескольких случайностей. Одна из которых — неправильно созданное продолжение (вы вызываете alert сразу же вместо того чтобы передавать его как замыкание).

Да, какую-то фигню сморозил. Идея была в том, чтобы сделать код синхронным.

Если вам нужно исполнение alert('Stop'); строго после любых прямых продолжений — можно использовать setTimeout:


function doHomework(subject) {
  return new Promise(resolve => {
    alert(`Starting my ${subject} homework.`);
    resolve();
  })
  .then(() => new Promise(resolve => setTimeout(resolve, 0)))
  .then(() => alert('Stop'));
}

Но на самом деле так лучше не делать, ибо костыль. Если вам требуется дождаться окончания исполнения обратного вызова — значит, обещания для этого не подходят.

Потому что строкой
doHomework('math').then(alert('Finished my homework'));

вы заставляете alert выполниться прямо на месте, а в then уходит результат выполнения.
Необходимо обернуть alert в функцию одним из способов:
// ES5:
doHomework('math').then(function() {alert('Finished my homework')});
или 
doHomework('math').then(alert.bind(null, 'Finished my homework'));

// ES6
doHomework('math').then(() => alert('Finished my homework'));

И этого все равно будет недостаточно. Потому что продолжение вызывается асинхронно.

Всё равно оно неправильно работает (ожидаю выполнение alert('Stop') после alert('Finished my homework'))

Как-то припозднились. На дворе уже вовсю промисы и уже разворачивается async/await, а вы про коллбеки.

Вот только сами промисы создаются с передачей в конструктор фукнции в качестве параметра. То есть — callback

element.addEventListener('click', ()=> {...}) 

на промисы или async/await как будете переводить?
Пардон, ошибся. Тут не при чём, верно.
UFO just landed and posted this here

А что, в 2017 году девелоперы рождаются со встроенным пониманием JS callback'ов? Автор дал вполне прозрачный заголовок — кто в теме, может "пролистать" публикацию. Кто заинтересовался — welcome, как говорится. Научиться фильтровать нужную информацию от ненужной по заголовку не сложнее, чем понять callback'и.


И да, кто-то может написать в 2017 году толковую статью про анонимные функции и даже про циклы, которая может пригодиться кому-то другому. Кстати, у этой статьи на данный момент положительный рейтинг и 20 человек уже добавили ее в избранное.

UFO just landed and posted this here
UFO just landed and posted this here
Или вы не правы или я чего-то не знаю.
Callback функция, после выполнения асинхронной операции, будет помещена в ту же event loop очередь функций, где была и функция, зарегистрировавшая callback.
Поскольку event loop обрабатывается в один поток, то callback не будет извлечен и обработан из очереди event loop, пока изначальная не закончит работу (и все вызвавшие её выше по стеку). Даже если асинхронная операция завершилась во время работы изначальной функции, коллбек раньше не пройдет.
Я даже примитивов синхронизации не знаю: js-функция выполняется всегда одна (даже для веб-воркеров).
Простите за косноязычие и поправьте если я не прав.

Как и любая другая функция, Callback функция выполняется когда она была вызвана.

Да, но нет.
Callback-функция окажется в очереди выполнения функций.
Описания: Event Loop Explained
или с раздела «Очередь» Параллельная модель и цикл событий.
Изначальный метод, зарегистрировавший callback, в любом случае дойдет до конца, даже если асинхронная операция (сетевой запрос, таймаут и пр.) уже завершилась. Callback не будет выполняться сразу, а добавится в эту же очередь. А очередь обрабатывается последовательно и синхронно:
while(queue.waitForMessage()){
  queue.processNextMessage();
}


PS: Минус, конечно, важная вещь… Но хочется убедиться, что я правильно или неправильно понимаю механизм работы js-движков. Пока больше нигде не видел утверждений, что callback вызывается сразу.

Изначальный метод может не регистрировать callback, а просто вызвать его.

Да, спасибо — это тривиально, но мы про асинхронные операции (перечитайте комментарий AlexPu).
Я про однопоточный движок выполнения js-методов, ссылки на невозможность срабатывания коллбека посреди папского метода (да и любого другого) привел.
Про синхронизацию потоков при моей неправоте спрашивать не буду.

Вообще-то, AlexPu писал про синхронные операции.

UFO just landed and posted this here
UFO just landed and posted this here
Да, выше меня поправили. Я подумал про асинхронный вызов коллбека, по причине что синхронный объяснять слишком очевидно: когда вызовешь — тогда и будет, если вызовешь. Думал дело глубже и хитрее :)
UFO just landed and posted this here
Это не моя статья. Я просто хотел убедиться, что правильно понимаю работу js-движка. Неправильно поняв Ваш первый комментарий — я засомневался. Сомнения сняты, спасибо.
UFO just landed and posted this here

Спасибо, благодаря вашей статье узнал что такое промисы, и теперь внедряю их в свой код. Промисы действительно выглядят визуально более понятнее чем использование каллбэков.

UFO just landed and posted this here
Sign up to leave a comment.