Continuation Passing Style
Для избавления от головной боли воспользуемся библиотекой
github.com/caolan/async
Для работы будем использовать такую вещь как CPS.
Звучит гораздо страшнее, чем есть на самом деле. Это функция, которая получает в качестве параметра другую функцию, и когда первая функция завершается, она вместо retrun вызывает функцию-параметр.
Обернем jQuery.ajax таким образом, чтобы получить требуемый результат.
function ajax(url, callback){
jQuery.ajax({url: url, success: callback});
}
Функция получает в качестве параметера callback, и мы не описали обработчик ошибок. В реальном коде он обязан быть, но для простоты изложения, мы о нем забудем.
Что же будет, если использовать библиотеку async? Получится что-то типа такого:
function combine(scripts, callback){
async.map(scripts, ajax, function(contents){
callback(contents.join(""));
});
}
Мы имеем готовую функцию map, работающую в асинхронном мире. Кстати, текущая реализация обеспечит правильный порядок склейки скриптов, в отличие от нашего лобового примера.
Сравните с тем, что было:
function combine(scripts, callback){
var data [];
for(var i =0; l = scripts.length; i< l; ++i){
(function (i){
jQuery.ajax({
url: scripts[i],
success : function(response){
data[i] = response;
if(data.length === scripts.length){
callback(data.join(""));
}
}
});
}(i));
}
}
Поскольку map для меня уже является естественным способом написания программ, я бы никогда не написал кода приведенного выше. Я бы думал, как приспособить map к асинхронному окружению. И если бы не было библиотеки async, то написал бы асинхронный map сам.
Функциональный подход позволяет гораздо проще смотреть на вещи. И реализовывать более красивые решения.