Описание: Существуют ли какие-то хорошо зарекомендовавшие себя шаблоны наилучшей практики, которым я могу следовать, чтобы сохранить мой код читабельным, несмотря на использование асинхронного кода и обратных вызовов?
Я использую библиотеку JavaScript, которая делает много вещей асинхронно и сильно зависит от обратных вызовов. Кажется, что написание простого метода «load A, load B, ...» становится довольно сложным и трудным для понимания при использовании этого шаблона.
Позвольте мне привести (надуманный) пример. Допустим, я хочу загрузить кучу изображений (асинхронно) с удаленного веб-сервера. В C # / async я бы написал что-то вроде этого:
disableStartButton();
foreach (myData in myRepository) {
var result = await LoadImageAsync("http://my/server/GetImage?" + myData.Id);
if (result.Success) {
myData.Image = result.Data;
} else {
write("error loading Image " + myData.Id);
return;
}
}
write("success");
enableStartButton();
Компоновка кода следует «потоку событий»: сначала кнопка «Пуск» отключается, затем загружаются изображения ( await
гарантирует, что пользовательский интерфейс остается отзывчивым), а затем кнопка «Пуск» снова включается.
В JavaScript, используя обратные вызовы, я придумал это:
disableStartButton();
var count = myRepository.length;
function loadImage(i) {
if (i >= count) {
write("success");
enableStartButton();
return;
}
myData = myRepository[i];
LoadImageAsync("http://my/server/GetImage?" + myData.Id,
function(success, data) {
if (success) {
myData.Image = data;
} else {
write("error loading image " + myData.Id);
return;
}
loadImage(i+1);
}
);
}
loadImage(0);
Я думаю, что недостатки очевидны: мне пришлось переделать цикл в рекурсивный вызов, код, который должен быть выполнен в конце, где-то посередине функции, код, запускающий загрузку ( loadImage(0)
), находится в самом низу, и вообще это намного труднее читать и следовать. Это некрасиво, и мне это не нравится.
Я уверен, что я не первый, кто сталкивается с этой проблемой, поэтому мой вопрос таков: существуют ли какие-то хорошо зарекомендовавшие себя образцы наилучшей практики, которым я могу следовать, чтобы сохранить мой код читабельным, несмотря на использование асинхронного кода и обратных вызовов?
LoadImageAsync
на самом деле это вызовExt.Ajax.request
Sencha Touch.async.waterfall
это ваш ответ.Ответы:
Маловероятно, что вы можете достичь с простым js того же уровня лаконичности и выразительности в работе с обратными вызовами, что и в C # 5. Компилятор выполняет всю работу по написанию всех этих шаблонов для вас, и пока среда выполнения js не сделает этого, вам все равно придется время от времени передавать обратный вызов.
Тем не менее, вы не всегда хотите доводить обратные вызовы до уровня простоты линейного кода - перебрасывание функций не должно быть уродливым, существует целый мир, работающий с этим типом кода, и они остаются нормальными без
async
иawait
.Например, используйте функции высшего порядка (мой js может быть немного ржавым):
источник
Мне потребовалось немного, чтобы расшифровать, почему вы делаете это таким образом, но я думаю, что это может быть близко к тому, что вы хотите?
Сначала выполняется столько запросов AJAX, сколько он может выполнить одновременно, и ждет, пока все они не будут выполнены, прежде чем активировать кнопку «Пуск». Это будет быстрее, чем последовательное ожидание, и, я думаю, намного легче следовать.
РЕДАКТИРОВАТЬ : Обратите внимание, что это предполагает, что у вас есть в
.each()
наличии, иmyRepository
это массив. Будьте внимательны, какую итерацию цикла вы используете здесь вместо этого, если она недоступна - в этом используются свойства замыкания для обратного вызова. Я не уверен, что у вас есть в наличии, так как,LoadImageAsync
кажется, является частью специализированной библиотеки - я не вижу результатов в Google.источник
.each()
наличии, и теперь, когда вы упомянули об этом, нет необходимости выполнять загрузку последовательно. Я обязательно попробую ваше решение. (Хотя я приму ответ вски, так как он ближе к оригинальному, более общему вопросу.)Отказ от ответственности: этот ответ не отвечает конкретно на вашу проблему, это общий ответ на вопрос: «Существуют ли какие-то устоявшиеся шаблоны наилучшей практики, которым я могу следовать, чтобы сохранить мой код читабельным, несмотря на использование асинхронного кода и обратных вызовов?»
Из того, что я знаю, нет "устоявшейся" схемы, чтобы справиться с этим. Тем не менее, я видел два вида методов, используемых для того, чтобы избежать кошмаров вложенных обратных вызовов.
1 / Использование именованных функций вместо анонимных обратных вызовов
Таким образом, вы избегаете вложения, отправляя логику анонимной функции в другую функцию.
2 / Использование библиотеки управления потоками. Мне нравится использовать Step , но это просто вопрос предпочтений. Кстати, именно этим пользуется LinkedIn.
Я использую библиотеку управления потоками, когда использую много вложенных обратных вызовов, потому что она намного удобнее для чтения, когда в ней много кода.
источник
Проще говоря, JavaScript не имеет синтаксического сахара
await
.Но переместить «конечную» часть в нижнюю часть функции легко; и с немедленным выполнением анонимной функции мы можем избежать объявления ссылки на нее.
Вы также можете передать «конечную» часть как функцию обратного вызова для функции.
источник