Задача на 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, время: 16:41. |