Страницы

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

вторник, 31 декабря 2019 г.

Синхронизация выполнения функций

#javascript


Синхронизация в javascript. Например, есть три функции:

func1(function(){}); // асинхронная с callback
func2(function(){}); // асинхронная с callback
func3(); // синхронная возвращает true-false


Нужно, чтобы после того, как func1 и func2 вызвали свои callback, а
func3 вернул true, вызвать функцию func4.

Как правильно и лучше всего это сделать, если ли стандартная техника для такого?
    


Ответы

Ответ 1



Вариант 1 - наиболее разумный - использовать Promise и Deferred. http://api.jquery.com/promise/ http://api.jquery.com/jQuery.when/ http://api.jquery.com/deferred.promise/ Код: var d1 = $.Deferred(); var d2 = $.Deferred(); var d3 = $.Deferred(); func1(function() { d1.notify(); }); func2(function() { d2.notify(); }); func3(); d3.notify(); $.when( d1, d2, d3 ).done(function () { // Выполнить результирующее действие. }); Вариант 2 - завести счетчик количества выполняемых функций (не требует библиотек) и отслеживать выполнение в общем callback-е. Возможно также вместо счетчика использовать массив с именами функций. var currentCallbacks = 0; var expectedCallbacks = 3; var commonCallback = function() { currentCallbacks++; if (currentCallbacks == expectedCallbacks) { // Выполнить результирующее действие. } }; func1(function(){commonCallback();}); func2(function(){commonCallback();}); func3(); commonCallback(); Вариант 3 - _underscore.js имеет функцию after. Если библиотека уже подключена, можно использовать её функционал. Вернёт копию функции function, модифицированную таким образом, что она будет запущена только после того, как будет вызвана count раз. Удобно использовать при работе с асинхронными запросами, например, чтобы убедиться, что все обращения к серверу завершились. var commonCallback = _.after(3, function() { // Выполнить результирующее действие... }); func1(function() { commonCallback(); }); func2(function() { commonCallback(); }); func3(); // Внутри должен перед выходом из функции вызываться commonCallback(); var functions = [func1, func2, func3]; var runProcess = function() { _.each(functions, function(func) { func(); }); }; runProcess(); Вариант 4 - построить цепочку вызовов, но убивает все плюсы параллелизма. func1(callback1); function callback1() { ... func2(callback2); } function callback2() { ... func3(); // Выполнить результирующее действие. }

Ответ 2



Два варианта, довольно простые, «в лоб» – минимум кода, без дополнительных библиотек, обёрток и требований поддержки новых стандартов JavaScript: Счётчик, который инкрементировать после выполнения каждой из функций, и проверять сразу, равен ли он уже числу функций (3)? var done = 0; // счётчик завершённых задач func1(function(){ ..; areWeDone()}); // асинхронная с callback func2(function(){ ..; areWeDone()}); // асинхронная с callback func3(){ ..; areWeDone()}; // синхронная возвращает true-false function areWeDone() { if( ++done === 3} { // все три выполнены } else { // ещё нет } } Чуть сложнее: завести битовую маску, где каждый бит означает выполнение одной из функций. Будет точно известно, какие именно ф-ии уже выполнились, какие ещё нет. Можно строить более развитую логику. var done = 0; // биты 0,1,2 - готовность от func1, func2, func3 func1(function(){ ..; areWeDone(0)}); // асинхронная с callback func2(function(){ ..; areWeDone(1)}); // асинхронная с callback func3(){ ..; areWeDone(2)}; // синхронная возвращает true-false function areWeDone(bit) { done = done | 1<

Ответ 3



Еще вариант с использованием Promise. Подойдет если используемые функции действительно асинхронные и сами не возвращают Promise или аналогов из используемых библиотек. Для использования нужно обернуть используемые функции в объект Promise: Вариант для функций с callback var p1 = new Promise(function(resolve, reject) { func1(function() { ... resolve(); // говорим, что все завершилось успешно. В случае неудачи нужно вызывать reject }); // ассинхронная с callback }); Для синхронной функции которая возвращает значение, которое нужно проверить var p = new Promise(function(resolve, reject) { if (func3() == condition) { // если условие выполняется resolve(); // говорим, что все завершилось успешно. } else { //если нет reject(); // говорим, что все завершилось неудачно. } }); Для синхронной функции: Promise.resolve(func()) После оборачивания в объекты для ожидания результата всех нужно использовать Promise.all Promise.all([p1, p2, ...]) Данная функция вернет Promise которые перейдет в статус fulfilled, т.е. завершен успешно, когда все Promise переданные в массиве будут иметь статус fulfilled Тогда с помощью метода.then можно добавить обработчик Promise.all(...).then( function(){ ... },//функция которая выполнится если статус _fulfilled_ function(){} //функция которая выполнится если статус _rejected_ ) Пример: function func1(c) { setTimeout(c, 100); } function func2(c) { setTimeout(c, 10); } function func3() { return true; } var p1 = new Promise(function(resolve, reject) { func1(function() { output('1'); resolve(); }); // ассинхронная с callback }); var p2 = new Promise(function(resolve, reject) { func2(function() { output('2'); resolve(); }); // ассинхронная с callback }); var p3 = new Promise(function(resolve, reject) { if (func3()) { output('3'); resolve(); // синхронная возвращает true-false } else { reject(); } }); Promise.all([p1, p2, p3]).then(function() { output('All done'); }); function output(text) { console.log(text); document.getElementById('res').innerHTML += text + '
'; }


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

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