Итак, мы пишем приложение с кучей асинхронных запросов. Нам надо отправить два асинхронных запроса и обработать их результат только после того, как будет получен результат обоих. Например, это могут быть ассинхронные обращение к файлу и запрос к базе, результат которых надо сложить вместе и обработать. Или два аджакс запроса.
Но особенность асинхронных запросов в том, что мы не знаем, какой из них придёт первым, а какой — последним. Решают это разными способами, но я не видел еще красивого и изящного. В топике я расскажу, как я это вижу.
Обычно поступают по одному из следующих вариантов:
Обычно такое могут сделать только люди, которые совершенно не понимают ассинхронные вызовы. Как-то так:
Представим, что обращение с файловой системой чуть затормозилось и запрос от базы пришёл быстрее. Тогда в fileData ничего не будет, хотя мы просто не длождались этих данных! Печально…
Другой вариант — как минимум надежный. Совершаем один запрос, ждем пока он завершится, совершаем второй запрос.
Это лучше, чем надежда на лучшее, но тут мы жертвуем временем — база бы могла уже искать, пока происходит обращение к файловой системе, а нет — приходится сидеть, сложа руки, ждать. ��, в случае с аджаксом, пользователь будет ждать ответ в два раза дольше. Нехорошо
Система флагов или проверка переменных. После каждого запроса отмечаем флаг, соответствующий ему и, если все флаги отмечены — выполняем. самый правильный вариант.
Но не очень красивый. Код повторяется, флаги надо дописывать во много мест. Если надо послать три одновременных запроса — будет дофига текст
Так лучше, но тоже не очень приятно. Флаги повторяются 4 раза (посчитайте количество переменных fileData)
Код, в результате, такой:
Или, даже, такой:
По сути мы воспользуемся вариантом с флагами, но абстрагируем его под методом, который добавим в прототип функции:
Думаю, это достаточно удобный и красивый способ для такой редкой, но важной задачи.
Но особенность асинхронных запросов в том, что мы не знаем, какой из них придёт первым, а какой — последним. Решают это разными способами, но я не видел еще красивого и изящного. В топике я расскажу, как я это вижу.
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; };
Думаю, это достаточно удобный и красивый способ для такой редкой, но важной задачи.
