Pull to refresh

После всех асинхронных вызовов

Reading time3 min
Views8.4K
Итак, мы пишем приложение с кучей асинхронных запросов. Нам надо отправить два асинхронных запроса и обработать их результат только после того, как будет получен результат обоих. Например, это могут быть ассинхронные обращение к файлу и запрос к базе, результат которых надо сложить вместе и обработать. Или два аджакс запроса.
Но особенность асинхронных запросов в том, что мы не знаем, какой из них придёт первым, а какой — последним. Решают это разными способами, но я не видел еще красивого и изящного. В топике я расскажу, как я это вижу.
var process = processFsAndDb.after('fs', 'db');

asyncFsAccess( file, process.fs);
asyncDbAccess(query, process.db);


Обычно поступают по одному из следующих вариантов:

Авось повезет


Обычно такое могут сделать только люди, которые совершенно не понимают ассинхронные вызовы. Как-то так:
var fileData = null;

asyncFsAccess( file, function (data) {
	fileData = data;
});
asyncDbAccess(query, function (dbData) {
	processFsAndDb(fileData, dbData);
});

Представим, что обращение с файловой системой чуть затормозилось и запрос от базы пришёл быстрее. Тогда в fileData ничего не будет, хотя мы просто не длождались этих данных! Печально…

Фиг с ним, подожду


Другой вариант — как минимум надежный. Совершаем один запрос, ждем пока он завершится, совершаем второй запрос.
asyncFsAccess( file, function (fileData) {
	asyncDbAccess(query, function (dbData) {
		processFsAndDb(fileData, dbData);
	});
});

Это лучше, чем надежда на лучшее, но тут мы жертвуем временем — база бы могла уже искать, пока происходит обращение к файловой системе, а нет — приходится сидеть, сложа руки, ждать. А, в случае с аджаксом, пользователь будет ждать ответ в два раза дольше. Нехорошо

7 раз проверь


Система флагов или проверка переменных. После каждого запроса отмечаем флаг, соответствующий ему и, если все флаги отмечены — выполняем. самый правильный вариант.
var fileData = dbData = null;

asyncFsAccess( file, function (data) {
	fileData = data;
	if (fileData && dbData)
		processFsAndDb(fileData, dbData);
});
asyncDbAccess(query, function (data) {
	dbData   = data;
	if (fileData && dbData)
		processFsAndDb(fileData, dbData);
});

Но не очень красивый. Код повторяется, флаги надо дописывать во много мест. Если надо послать три одновременных запроса — будет дофига текст

7 раз проверь ++


var fileData = dbData = null;

var process = function () {
	if (fileData && dbData)
		processFsAndDb(fileData, dbData);
};

asyncFsAccess( file, function (data) {
	fileData = data;
	process();
});
asyncDbAccess(query, function (data) {
	dbData   = data;
	process();
});

Так лучше, но тоже не очень приятно. Флаги повторяются 4 раза (посчитайте количество переменных fileData)

Мой вариант, расширяем прототип


Код, в результате, такой:
var process = function (args) {
	processFsAndDb(args.fs, args.db);
}.after('fs', 'db');

asyncFsAccess( file, process['fs']);
asyncDbAccess(query, process['db']);


Или, даже, такой:
var process = processFsAndDb.after('fs', 'db');

asyncFsAccess( file, process.fs);
asyncDbAccess(query, process.db);


По сути мы воспользуемся вариантом с флагами, но абстрагируем его под методом, который добавим в прототип функции:
Function.prototype.after = function () {
	/**
	 * @var onReady - это наша ф-ция, которая будет выпонлятся по завершению всех вызовов
	 * @var after   - это хэш функций, которые должны быть выполнены.
	 *     именно его свойства мы передаём как колбеки (process.fs и process.db)
	 * @var ready - здесь мы будем хранить ответы каждой функции и когда все ключи,
	 *     которые есть в after будут и тут - значит, пора вызывать onReady
	 */
	var onReady = this, after = {}, ready = {};
	var checkReady = function () {
		for (var i in after) if (!(i in ready)) return;
		onReady(ready);
	};
	for (var i = 0, l = arguments.length; i < l; i++) {
		(function (key) {
			after[key] = function () {
				ready[key] = arguments;
				checkReady();
			};
		})(arguments[i]);
	}
	return after;
};


Думаю, это достаточно удобный и красивый способ для такой редкой, но важной задачи.
Tags:
Hubs:
+63
Comments114

Articles