Задача на ассинхронность
Даны 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);
Для последовательного выполнения асинхронных функций нужно составить цепочку 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));
Если я правильно понял задачу, то тут задача, по сути, "перебить" случайную задержку. Для этого можно использовать обёртку, возвращающую промис. Внутри промиса запускаем наши функции, а в качестве колбэка передаём функцию, которая получит из промиса 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 дозаписывать значения в неё, а по окончании вызовов, вывести в консоль