Задача на ассинхронность

Даны 3 асинхронные функции со случайным setTimeout Нужно написать код, который выведет в консоль: A B C

function foo(callback) {
  setTimeout(function () {
    callback("A");
  }, Math.random() * 100);
}

function bar(callback) {
  setTimeout(function () {
    callback("B");
  }, Math.random() * 100);
}

function baz(callback) {
  setTimeout(function () {
    callback("C");
  }, Math.random() * 100);
}

Ответы (5 шт):

Автор решения: Михаил В

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

function foo(callback) {
  setTimeout(function () {
    callback("A");
  }, Math.random() * 100);
}

function bar(callback) {
  setTimeout(function () {
    callback("B");
  }, Math.random() * 100);
}

function baz(callback) {
  setTimeout(function () {
    callback("C");
  }, Math.random() * 100);
}

window.setTimeout = function(func, timeout) {
    func();
}

foo(console.log);
bar(console.log);
baz(console.log);

→ Ссылка
Автор решения: Grundy

Для последовательного выполнения асинхронных функций нужно составить цепочку Promise.

Для этого можно добавить функцию, которая вернет Promise, разрешающийся, в момент вызова callback

function promisify(func) {
  return new Promise(r => func(r));
}

после этого, достаточно передавать в нее нужную функцию, составляя цепочку:

promisify(foo)
  .then(r => console.log(r))
  .then(() => promisify(bar))
  .then(r => console.log(r))
  .then(() => promisify(baz))
  .then(r => console.log(r))

Пример:

function foo(callback) {
  setTimeout(function() {
    callback("A");
  }, Math.random() * 1000);
}

function bar(callback) {
  setTimeout(function() {
    callback("B");
  }, Math.random() * 500);
}

function baz(callback) {
  setTimeout(function() {
    callback("C");
  }, Math.random() * 500);
}

function promisify(func) {
  return new Promise(r => func(r));
}

promisify(foo)
  .then(r => console.log(r))
  .then(() => promisify(bar))
  .then(r => console.log(r))
  .then(() => promisify(baz))
  .then(r => console.log(r))

→ Ссылка
Автор решения: Алексей Обухов

Замечательное решение от Grundy. Очень короткое и простое.
Но я хотел бы свое привести, более громозкое и сумбурное.

Сначала foo, bar, baz заменим одной функцией f:

function f(s, cb) {
    setTimeout(_ => cb(s), Math.random() * 100);
}

Попробуем вызвать их друг за другом:

f("A", console.log);
f("B", console.log);
f("C", console.log);

Получим случайный ответ, например, CAB
Исправим это, сделав вложенные вызовы "пирамидкой":

f("A", s => { 
    console.log(s); 
    f("B", s => {
        console.log(s);
        f("C", s => {
            console.log(s);
        })
    })
});

Получили правильный ответ, но хотим "выпрямить" код в плоский.
Для начала преобразуем f в промис:

let f1 = promisify(f);
function promisify(f) {
    return function(...args) {
        return new Promise(resolve => {
            function cb(res) {
                resolve(res);
            }
            args.push(cb);
            f.call(this, ...args);
        });
    }
}

Код взят отсюда, если вам promisify не нужна, можно сразу сделать замену f:

let f1 = function(s) {
    return new Promise(resolve => f(s, () => resolve(s)));
}

Либо можно повторить код f:

function f1(s) {
    return new Promise(resolve => setInterval(resolve(s), Math.random() * 100));
}

Выбор за вами, как вы сделаете функцию, возвращающую промис. Теперь можно воспользоваться await:

(async function() {
    await f1("A").then(r => console.log(r));
    await f1("B").then(r => console.log(r));
    await f1("C").then(r => console.log(r));
})();

Получаем ABC и вложенностей нет

Не хотите async/await, можно обойтись одним then:

f1("A")
    .then(r => console.log(r))
    .then(() => f1("B"))
    .then(r => console.log(r))
    .then(() => f1("C"))
    .then(r => console.log(r));

Также был предложен вариант запуска в параллель:

Promise.all([f2("A"), f2("B"), f2("C")]).then(rs => rs.forEach(r => console.log(r)));

Еще раз отмечу, что использовать асинхронность напрямую с обычными функциями (не возращающих промис) не получится. Их надо либо переписать через промис или промисифицировать (см. выше)

→ Ссылка
Автор решения: Дмитрий Краснов

Почему-то все отвечающие забыли, что требуется вывести в консоль "A B C" т.е. в одну строчку, а не в столбик.

У меня вышло 2 варианта.

На колбеках:

function foo(callback) {
    setTimeout(function () {
        callback("A");
    }, Math.random() * 100);
}

function bar(callback) {
    setTimeout(function () {
        callback("B");
    }, Math.random() * 100);
}

function baz(callback) {
    setTimeout(function () {
        callback("C");
    }, Math.random() * 100);
}

foo(
    (a) => bar(
        (b) => baz(
            (c) => console.log(a, b, c)
        )
    )
);

На промисах:

function foo(callback) {
    setTimeout(function () {
        callback("A");
    }, Math.random() * 100);
}

function bar(callback) {
    setTimeout(function () {
        callback("B");
    }, Math.random() * 100);
}

function baz(callback) {
    setTimeout(function () {
        callback("C");
    }, Math.random() * 100);
}

let a, b;

new Promise(resolve => foo(resolve))
    .then(data => {
        a = data;
        return new Promise(resolve => bar(resolve));
    })
    .then(data => {
        b = data;
        return new Promise(resolve => baz(resolve));
    })
    .then(data => console.log(a, b, data));

→ Ссылка
Автор решения: Ben Barnes

Если я правильно понял задачу, то тут задача, по сути, "перебить" случайную задержку. Для этого можно использовать обёртку, возвращающую промис. Внутри промиса запускаем наши функции, а в качестве колбэка передаём функцию, которая получит из промиса resolve и выполнит его после console.log. От нас требуется только запустить в правильном порядке. Вот что получилось у меня:

function foo(callback) {
  setTimeout(function () {
    callback("A");
  }, Math.random() * 100);
}

function bar(callback) {
  setTimeout(function () {
    callback("B");
  }, Math.random() * 100);
}

function baz(callback) {
  setTimeout(function () {
    callback("C");
  }, Math.random() * 100);
}

function wrapper(func) {
  const cback = (rslv) => (param) => {
    console.log(param);
    rslv();
  };
  return new Promise((resolve) => {
    func(cback(resolve));
  });
}

(async () => {
  await wrapper(foo);
  await wrapper(bar);
  await wrapper(baz);
})();

Если же необходимо вывести всё в одну строку, то проще всего использовать для значений глобальную переменную, а вместо console.log дозаписывать значения в неё, а по окончании вызовов, вывести в консоль

→ Ссылка