Pull to refresh

Синхронизация асинхронных вызовов. WaitSync

Reading time 3 min
Views 8.6K

Задача


Допустим вы хотите выполнить два или более AJAX запроса на сервер и вызвать функцию после того, как все они будут закончены.

Небольшая загвоздка в том, что вы не знаете, какой из этих запросов будет закончен первым и не знаете, на кого вешать callback.

Можно завести глобальную переменную и по окончании каждого запроса проверять, завершился ли другой запрос.

Собственно, этим и занимается мой небольшой класс WaitSync =)

WaitSync.js


Пользоваться элементарно просто:

1. Создаем объект типа WaitSync, передав в конструктор callback функцию, которая будет вызвана после того, как отработают нужные задачи.
	var vulture = new WaitSync(function () {
		console.debug('Start eating: ', arguments);
	});


2. Вместо простого
	$.getJSON(
		'savannah/get_prey', 
		function (data) {
			console.log('... prey found: ' + data);
		}
	);
	
	$.getJSON(
		'savannah/get_other_predators', 
		function () {
			console.log('... predators are done eating');
		}
	);


«заворачиваем» задачи в метод .wrap
	$.getJSON(
		'savannah/get_prey', 
		vulture.wrap( 
			function (data) {
				console.log('... prey found: ' + data);
			}
		)
	);
	
	$.getJSON(
		'savannah/get_other_predators', 
		vulture.wrap(
			function () {
				console.log('... predators are done eating');
			}
		)
	);


3. Все =) Как только будут выполнены оба AJAX запроса, стервятник начнет есть.


Как это работает?



Когда вы создаете объект WaitSync, вы передаете в качестве аргумента конструктору
тот самый callback.

Когда вы вызываете метод .wrap и передаете ему функцию, он создает из нее другую функцию, которая делает подсчет и проверку вызовов. В качестве второго аргумента также можно передать контекст (ссылку на объект, к которому можно будет обращаться через this). Если вам это действительно нужно…

То есть, фактически, класс делает за вас то, что вы сделали бы своими руками разок-другой, а потом решили бы написать нечто подобное :)

Именованные/группированные задачи


Предположим, вы делаете ajax запросы и вдобавок хотите обработать ошибки:

	var monkeyBusiness = new WaitSync(function () {});
	
	$.ajax({
		url: '/cgi-bin/palm-tree',
		success: function (data) {
			console.log(' eat bananas :) ');
			
		},
		error: function (data) {
			console.log(' eat banana peels :( ');
		}
	});
	
	$.ajax({
		url: '/cgi-bin/monkey-party',
		success: function (data) {
			console.log(' dance :) ');
		},
		error: function (data) {
			console.log(' make political party :( ');
		}
	});
	


Всегда будет выполнятся либо success, либо error. В случае возникновения любого из них нужно перестать ждать появления второго.

Для этого достаточно просто придумать для задачи идентификатор (строковый или числовой) и передать его первым параметром .wrap

	
	var monkeyBusiness = new WaitSync(function () {
		console.log('Yay!  :(|) End of the day');
	});
	
	$.ajax({
		url: '/cgi-bin/palm-tree',
		success: 
			monkeyBusiness.wrap(
				'palmTree',
				function (data) {
					console.log(' eat bananas :) ');
					
					return "banana success";
				}
			),
		error: 
			monkeyBusiness.wrap(
				'palmTree',
				function (data) {
					console.log(' eat banana peels :( ');
					return "banana failure";
				}
			)
		
	});
	
	$.ajax({
		url: '/cgi-bin/monkey-party',
		success:
			monkeyBusiness.wrap(
				'monkeyParty',
				function (data) {
					console.log(' dance :) ');
					
					return "success";
				}
			),
		error: 
			monkeyBusiness.wrap(
				'monkeyParty',
				function (data) {
					console.log(' make political party :( ');
					
					return "error";
				}
			)
	});
	


Теперь callback выполнится как только мартышка слезет с пальмы, а другие закончат вечеринку.

Бонус!


На самом деле WaitSync собирает еще кое-какую информацию.

В callback передается объект, содержащий немного полезной информации:
	
	var monkeyBusiness = new WaitSync(
		function (result) {
			console.log('Tasks were complete in the following order: ' + result.order.join(', '));
			
			console.log('Palm tree: ' + result.data['palmTree']);
			console.log('Monkey party: ' + result.data['monkeyParty']);
		}
	);
	


В подмассиве order хранится порядок, в котором выполнились задачи.

В подмассиве groupOrder хранится порядок только именованных задач/групп.

В подмассиве data хранятся значения, которые вернули все именнованные задачи.

Заключение


Думаю, такая вещь достаточно редко нужна. В node.js могла бы пригодиться…

В общем, вот =) Пользуйтесь на здоровье!

github.com/TEHEK/waitsync

P.S.: пока писал пост, два раза переписывал API XD

P.P.S.: после того, как опубликовал, заметил топик на ту же самую тему, опубликованный ранее… Спасибо The Shock, за оставленное мнение и хайлайт))
Tags:
Hubs:
+30
Comments 63
Comments Comments 63

Articles