Страницы

Поиск по вопросам

четверг, 5 декабря 2019 г.

Как выполнить последовательно несколько асинхронных фунций в Javascript без коллбэков?

#javascript #jquery #асинхронность #promise


Как написать простой, понятный, легко обслуживаемый код, который запускает последовательно
несколько асинхронных функций в javascript/jQuery?
(когда отработает одна, должна запускаться другая)

Следующий пример иллюстриует мой вопрос:

function f1(){ setTimeout( function(){ console.log(1); }, 30); }
function f2(){ setTimeout( function(){ console.log(2); }, 20); }
function f3(){ setTimeout( function(){ console.log(3); }, 10); }
f1(); f2(); f3(); 


на выходе 3 2 1
как сделать что-бы выдавало 1 2 3  ?

Желательно без коллбеков - т.к. если надо запустить последовательно больше двух функций
это уже тяжело читать.
Многое говорит о том что возможно решение с помощью обьекта $.Deferred, но пока не
видел разумного варианта.

Подобный вопрос задавался не раз, но я почему-то не нашел ответа который бы меня устроил.
    


Ответы

Ответ 1



Если вы хотите использовать Обещания (Promise), то для начала вам нужно модифицировать ваши функции так, чтобы они возвращали Обещания. Например, первая из ваших функций будет иметь вид: function f1() { return new Promise(function(resolve){ setTimeout(function() { console.log(1); resolve(); }, 30); }); } Остальные функции преобразуются аналогичным образом. Теперь у вас есть три функции (f1, f2, f3), которые возвращают Обещания и вы хотите выполнить их последовательно. Если вы не используете библиотек, вроде Bluebird, то вам придется реализовать очередь вызова Обещаний вручную. Это не так сложно, как кажется: // Аргумент "deeds" - это массив функций, которые должны выполняться // последовательно. При этом, каждая функция должна возвращать // Обещание (Promise). var seqRunner = function(deeds) { return deeds.reduce(function(p, deed) { return p.then(function() { // Выполняем следующую функцию только после того, как отработала // предыдущая. return deed(); }); }, Promise.resolve()); // Инициализируем очередь выполнения. } А пользоваться этой очередью нужно вот так: seqRunner([f1, f2, f3]).then(function() { console.log('Done!'); }); А вот и JSFiddle с рабочим примером. Замечание: Если у вас заранее известное, небольшое число функций, то можно вообще обойтись без функции seqRunner и связывать функции вручную: f1().then(function() { return f2(); }).then(function() { return f3(); }).then(function() { console.log('Done!'); });

Ответ 2



Альтернативный вариант, сделать обертку над setTimeout возвращающую Promise, например так: function delay(timeout){ return new Promise(function(r){ setTimeout(r,timeout); }); } Теперь код из вопроса может выглядеть следующим образом: function delay(timeout) { return new Promise(function(r) { setTimeout(r, timeout); }); } function f1() { console.log(1); } function f2() { console.log(2); } function f3() { console.log(3); } delay(3000).then(f1) .then(function(){return delay(2000);}) .then(f2) .then(function(){ return delay(1000);}) .then(f3) .then(function() { console.log('all finish'); }); Или даже так function delay(timeout) { return new Promise(function(r) { setTimeout(r, timeout); }); } function f1() { console.log(1); return 2000 } function f2() { console.log(2); return 1000; } function f3() { console.log(3); } delay(3000).then(f1) .then(delay) .then(f2) .then(delay) .then(f3) .then(function() { console.log('all finish'); }); Если таких функций много, то можно собрать из них массив и с помощью функции reduce собрать один большой Promise function delay(timeout) { return new Promise(function(r) { setTimeout(r, timeout); }); } function f1() { console.log(1); return 2000 } function f2() { console.log(2); return 1000; } function f3() { console.log(3); } [f1, f2, f3].reduce(function(promise, func) { return promise.then(func).then(delay); }, delay(3000)) .then(function() { console.log('all finish') });

Ответ 3



Если каждой добавить таки callback отчёт о выполнении, можно держать очередь в массиве и вызывать следующую ф-ю по завершению предыдущей: function f1(){ setTimeout( function(){ console.log(1); next(); }, 30); } function f2(){ setTimeout( function(){ console.log(2); next(); }, 20); } function f3(){ setTimeout( function(){ console.log(3); next(); }, 10); } var queue = [f1, f2, f3] ,i=0 ,next = function(){ queue[i] && queue[i++]();} ; next(); // 1 2 3

Комментариев нет:

Отправить комментарий