Задача на Event Loop
Добрый день! Столкнулся с интересной задачей и не знаю как решить.
function one(callback) {
setTimeout( function() {
callback("First");
}, Math.random() * 100);
}
function two(callback) {
setTimeout( function() {
callback("Second");
}, Math.random() * 100);
}
function three(callback) {
setTimeout( function() {
callback("Third");
}, Math.random() * 100);
}
function runCallback(s) {
console.log(s);
}
Нужно вызвать функции one, two и three в правильной последовательности, чтобы в консоль вывелось A, B и C в нужном порядке. При этом использовать можно все, даже дописывать runCallback, но сами функции менять нельзя. :help: |
Эту задачу можно понимать по разному.
В простейшем варианте вы вызываете следующую функцию из runCallback когда закончилась предыдущая. Но можно и такое изобразить:
function stream(log, ...args) {
let index = 0;
const results = new Map();
const callback = (i, result) => {
if(i === index) {
while(true) {
log(result);
if(!results.has(++index))
break;
result = results.get(index);
results.delete(index);
}
} else {
results.set(i, result);
}
};
args.forEach((func, i) => func(result => callback(i, result)));
}
stream(runCallback, one, two, three)
получив максимальную возможную скорость вызовов. |
Понял, спасибо! :)
|
Предложу решение "в лоб"...
<!DOCTYPE html>
<html>
<head>
<!--
<script src='https://code.jquery.com/jquery-latest.min.js'></script>
-->
<style type='text/css'>
</style>
<script>
function one(callback) {
setTimeout( function() {
callback("First");
}, Math.random() * 2000);
}
function two(callback) {
setTimeout( function() {
callback("Second");
}, Math.random() * 2000);
}
function three(callback) {
setTimeout( function() {
callback("Third");
}, Math.random() * 2000);
}
function runCallback(s) {
const o = document.createElement('p')
o.textContent = s
document.querySelector('body').appendChild(o)
}
// Вот собственно решение...
const test1 = res => {
runCallback(res)
three(runCallback)
}
const test2 = res => {
runCallback(res)
two(test1)
}
one(test2)
</script>
</head>
<body>
</body>
</html>
|
Или вовсе так...
<!DOCTYPE html>
<html>
<head>
<!--
<script src='https://code.jquery.com/jquery-latest.min.js'></script>
-->
<style type='text/css'>
</style>
<script>
function one(callback) {
setTimeout( function() {
callback("First");
}, Math.random() * 2000);
}
function two(callback) {
setTimeout( function() {
callback("Second");
}, Math.random() * 2000);
}
function three(callback) {
setTimeout( function() {
callback("Third");
}, Math.random() * 2000);
}
function runCallback(s) {
const o = document.createElement('p')
o.textContent = s
document.querySelector('body').appendChild(o)
}
// Вот собственно решение...
one(res => {
runCallback(res)
two(res => {
runCallback(res)
three(runCallback)
})
})
</script>
</head>
<body>
</body>
</html>
|
Я считаю предыдущие решения некорректными, т.к. при вызове функций используется другой callback. Не сам runCallback
Предлагаю свое решение
function one(callback) {
setTimeout( function() {
callback("First");
}, Math.random() * 1000);
}
function two(callback) {
setTimeout( function() {
callback("Second");
}, Math.random() * 1000);
}
function three(callback) {
setTimeout( function() {
callback("Third");
}, Math.random() * 1000);
}
function runCallback(s) {
console.log(s);
}
let console;
function call (...funs) {
const cl = window.console.log
let i = 0
console = {
log (...args) {
cl(...args)
if (i<funs.length)
funs[i++](runCallback)
else
console = window.console;
}
}
funs[i++](runCallback)
}
call(one, two, three)
|
Цитата:
имхо, раз уж runCallback и callback у функций не склеены изначально, то между ними и надо втыкать "мидлвар". А в целом задача решается в одну строку, если не городить велосипеды:
// ----- условие --------------
function one(callback) {
setTimeout( function() {
callback("First");
}, Math.random() * 100);
}
function two(callback) {
setTimeout( function() {
callback("Second");
}, Math.random() * 100);
}
function three(callback) {
setTimeout( function() {
callback("Third");
}, Math.random() * 100);
}
function runCallback(s) {
console.log(s);
}
// ----- решение --------------
Promise.all([one, two, three].map(f => new Promise(f))).then(r => r.forEach(runCallback));
|
Alexandroppolus, но вся суть в велосипедах.)
Твоя версия запускает все функции сразу и разом выводит всё когда получены все результаты. Время первого вывода на экран = Время выполнения самой медленной функции. Время последнего вывода на экран = Время выполнения самой медленной функции. Версии voraa и ksa запускают функции и выводят результат по порядку. Время первого вывода на экран = Время выполнения первой функции. Время последнего вывода на экран = Сумма времени выполнения всех функций. Мой велосипед запускает все функции сразу, но выводит по мере исполнения и по порядку.) Время первого вывода на экран = Время выполнения первой функции. Время последнего вывода на экран = Время выполнения самой медленной функции. В худшем случае: если первая функция - самая медленная, то результат будет как у тебя, но это крайний вариант. Примерно так работают async стримы, только там есть ещё ограничение на количество одновременно запущенных потоков, чтоб все ресурсы не съело.) |
Aetae, вариант со стримом прикольный, жизненный. Возможно, так и было в исходном условии, но Himmelin сыграл в испорченный телефон.
|
Aetae,
ksa, voraa, Alexandroppolus, :thanks: |
(function test(queue) {
const fn = queue.shift()
fn(s => {
runCallback(s)
if (queue.length) test(queue)
})
})([one, two, three])
пример с рекурсией :) |
Ну если воспользоваться идеями Aetae и Alexandroppolus и попытаться объединить их, то получается такой вариант
function one(callback) {
setTimeout( function() {
callback("First");
}, Math.random() * 1000);
}
function two(callback) {
setTimeout( function() {
callback("Second");
}, Math.random() * 500);
}
function three(callback) {
setTimeout( function() {
callback("Third");
}, Math.random() * 100);
}
function runCallback(s) {
console.log(s);
}
async function stream(log, ...args) {
const ap = args.map (func => new Promise(func))
for (let i = 0; i < args.length; i++) {
log(await ap[i])
}
}
stream(runCallback, one, two, three)
|
Тогда так на колбеках :)
function one(callback) {
setTimeout( function() {
callback("First");
}, Math.random() * 1000);
}
function two(callback) {
setTimeout( function() {
callback("Second");
}, Math.random() * 500);
}
function three(callback) {
setTimeout( function() {
callback("Third");
}, Math.random() * 100);
}
function runCallback(s) {
console.log(s);
}
const stream = (log, ...queue) => queue.shift()(s => (log(s), queue.length && stream(log, ...queue)))
stream(runCallback, one, two, three)
|
Цитата:
|
Цитата:
Цитата:
|
voraa, во, вот это мило.)
Ток можно вот так вот модняво записать, чтоб ваще:
async function stream(log, ...args) {
for await(let result of args.map(func => new Promise(func))) {
log(result)
}
}
Жаль в js нельзя как в жабе сделать так: args.map(Promise::new), тогдаб вообще идеально было.) |
Цитата:
|
тот же вариант без асинк/авайт:
function stream(log, ...args) {
args.reduce((acc, func) => {
const prom = new Promise(func);
return acc.then(() => prom).then(log);
}, Promise.resolve());
}
|
Alexandroppolus, да обычный расколабас. Те же классы которые через class не могут работать без new по спеке.
Всегда можно сделать хрень типа:
window.Promise = new Proxy(Promise, {
apply (target, _, args) {
return new target(...args);
}
});
но как-то оно... |
Пошла битва за буковки :haha:
const stream = (log, ...args) => args.reduce((a, f) => a.then(_ => new Promise(f)).then(log), Promise.resolve()); const stream = (log, ...args) => args.shift()(s => (log(s), args.length && stream(log, ...args))) |
Vlasenko Fedor,
у тебя последовательный вызов функций, не "стрим" в терминологии этого топика |
Alexandroppolus,
Цитата:
Цитата:
const q = (l, ...a) => a.shift()(s => (l(s), a[0] && q(l, ...a))) q(runCallback, one, two, three) |
Вот к этому решению я пытался прийти, знал что через промисы как-то можно, но чет не пришел(
|
Цитата:
Вот возьмем реальный стрим видео по http. Сервер пакеты посылает в правильной последовательности. Показывать их тоже надо в правильной последовательности. А вот приходят они иногда не всегда в правильной. И если есть возможность, то можно подождать задержавшийся пакет, особенно, если это опорный кадр |
| Часовой пояс GMT +3, время: 03:37. |