Ajax и асинхронная обработка
Метод Ajax, используемый для вызова API для доступа к данным, является асинхронным процессом. Самый простой метод обработки асинхронного процесса — это события или обратные вызовы. На самом деле два метода обработки имеют схожие принципы реализации. Интерфейс, который вызывается при завершении процесса. . например jQuery Ajaxsuccess
Это типичный параметр обратного вызова. Однако при использовании jQuery для обработки асинхронности рекомендуется использовать обработку промисов.
Обработка обещаний также выполняется путем регистрации функций обратного вызова. Промисы jQuery немного отличаются от стандартных промисов ES6, но вthen
совместим с вышеперечисленным, обычно называемым thenable. Обещания jQuery не предоставляются.catch()
интерфейс, но он определяет себя.done()
,.fail()
а также.always()
Три способа регистрации обратных вызовов также весьма своеобразны и очень удобны в использовании: они регистрируются как события (то есть можно зарегистрировать несколько обработчиков одного типа, и все они будут срабатывать при срабатывании).
Конечно, более интуитивный способ — использовать метод async/await, появившийся в ES 2017. Вы можете писать асинхронный код в виде синхронного кода, но, конечно, в нем есть некоторые подводные камни. Для front-end инженеров самая большая ловушка заключается в том, что некоторые браузеры не поддерживают его и должны быть переведены, поэтому, если во front-end коде нет процесса конструирования, то обычно лучше использовать синтаксическую совместимость с ES5 (jQuery Promise поддерживает ES5, но стандартные промисы можно использовать только после ES6).
Дополнительные сведения об асинхронной обработке JavaScript см.
- Шаг за шагом от небольшой темы к асинхронным вызовам JavaScript
- Асинхронные вызовы чата «сглажены»
- Из ада в рай: обратные вызовы Node переходят на async/await
- Понимание async/await в JavaScript
- Никогда не говорите, что обработка ошибок с синтаксисом async/await реализована без try-catch
Инкапсулируйте свои собственные служебные функции
В процессе работы с Ajax, хотя и существуют готовые библиотеки (такие как jQuery.ajax, axios и т.д.), он все-таки предназначен для общих целей, и все же громоздок в использовании. В проекте процесс вызова Api практически такой же. При правильном проектировании даже обработка ошибок будет такой же. Таким образом, вызовы Ajax в проекте могут быть дополнительно инкапсулированы, чтобы сделать их более удобными для использования в проекте. Его также легче модифицировать, если меняется способ интерфейса.
Например, для текущего интерфейса требуется вызов метода POST (не RESTful), а параметры должны включатьaction
, возвращаемые данные предоставляются в формате JSON, в случае возникновения ошибки, если она не является исключением сервера, будут возвращены определенные данные JSON, включая значение, не равное 0code
и необязательноmessage
Атрибуты.
Затем используйте jQuery, чтобы написать такой вызов Ajax, возможно, так
const apiUrl = "http://api.some.com/";
jQuery
.ajax(url, {
type: "post",
dataType: "json",
data: {
action: "login",
username: "uname",
password: "passwd"
}
})
.done(function(data) {
if (data.code) {
alert(data.message || "登录失败!");
} else {
window.location.assign("home");
}
})
.fail(function() {
alert("服务器错误");
});
Предварительная упаковка
В том же проекте такие вызовы Ajax в основном толькоdata
часть и.done
в обратном вызовеelse
Части разные, поэтому инкапсуляция один раз значительно уменьшит количество кода, вы можете инкапсулировать так
function appAjax(action, params) {
var deffered = $.Deferred();
jQuery
.ajax(apiUrl, {
type: "post",
dataType: "json",
data: $.extend({
action: action
}, params)
})
.done(function(data) {
// 当 code 为 0 或省略时,表示没有错误,
// 其它值表示错误代码
if (data.code) {
if (data.message) {
// 如果服务器返回了消息,那么向用户呈现消息
// resolve(null),表示不需要后续进行业务处理
alert(data.message);
deffered.resolve();
} else {
// 如果服务器没返回消息,那么把 data 丢给外面的业务处理
deferred.reject(data);
}
} else {
// 正常返回数据的情况
deffered.resolve(data);
}
})
.fail(function() {
// Ajax 调用失败,向用户呈现消息,同时不需要进行后续的业务处理
alert("服务器错误");
deffered.resolve();
});
return deferred.promise();
}
И вызов бизнес-уровня очень прост
appAjax("login", {
username: "uname",
password: "passwd"
}).done(function(data) {
if (data) {
window.location.assign("home");
}
}).fail(function() {
alert("登录失败");
});
Изменить интерфейс вызова API
Приведенная выше инкапсуляция обрабатывает вызывающий интерфейс и возвращаемые данные унифицированным образом, а также большую часть контрактного содержимого интерфейса проекта.
Теперь команда проекта решила не использовать Ajax jQuery, а использовать axios для вызова API (axios не обязательно лучше, чем jQuery, здесь просто пример), а затем просто изменить егоappAjax()
можно реализовать. Все деловые вызовы не нужно модифицировать.
Предполагая, что текущая целевая среда все еще ES5, требуется сторонний Promise.Здесь предлагается Bluebird, который совместим с родным интерфейсом Promise (представленным в HTML, а не непосредственно в коде JS).
function appAjax(action, params) {
var deffered = $.Deferred();
axios
.post(apiUrl, {
data: $.extend({
action: action
}, params)
})
.then(function(data) { ... }, function() { ... });
return deferred.promise();
}
Этот пакет использует axios для реализации вызовов веб-API. Но чтобы сохранить исходный интерфейс (объект jQuery Promise предоставляет.done()
,.fail()
а также.always()
обработка событий),appAjax
Все еще должен был вернуть обещание jQuery. Таким образом, хотя jQuery больше не нужен везде, его все равно придется использовать здесь.
Должен ли я использовать или не использовать jQuery в своем проекте? пожалуйста прочтиЗачем использовать нативный JavaScript вместо jQuery?
удалить jQuery
Простое использование jQuery здесь всегда кажется ошеломляющим и хочется избавиться от него. Есть два способа
- Изменить звонки во всех службах и удалить их
.done()
,.fail()
а также.always()
, изменился на.then()
. Этот шаг требует много работы, но в основном безболезнен, потому что сам jQuery Promise поддерживает.then()
. Но есть одна вещь, требующая особого внимания, о которой будет рассказано позже. - Самостоятельное написание адаптера, совместимого с интерфейсом jQuery Promise, — это немалый объем работы, но ключ в том, чтобы полностью протестировать его, чтобы избежать ошибок.
Одна вещь, которая требует особого внимания в первом методе, упомянутом выше, заключается в том, что.then()
а также.done()
Серийные функции отличаются тем, как они обрабатываются..then()
разработан в соответствии с характеристиками Promise, он возвращает другой объект Promise; и.done()
Ряд функций реализован в соответствии с механизмом событий и возвращает исходный объект Promise. Поэтому будьте осторожны при изменении кода, подобного следующему.
appAjax(url, params)
.done(function(data) { console.log("第 1 处处理", data) })
.done(function(data) { console.log("第 2 处处理", data) });
// 第 1 处处理 {}
// 第 2 处处理 {}
Проще говоря.done()
изменить на.then()
После этого (обратите внимание, нет необходимости использовать Bluebird, поскольку jQuery Promise поддерживает.then()
)
appAjax(url, params)
.then(function(data) { console.log("第 1 处处理", data); })
.then(function(data) { console.log("第 2 处处理", data); });
// 第 1 处处理 {}
// 第 2 处处理 undefined
Причина была упомянута выше, правильный способ справиться с этим здесь — объединить несколько готовых кодов, или в.then()
возврат в обработчикdata
:
appAjax(url, params)
.then(function(data) {
console.log("第 1 处处理", data);
return data;
})
.then(function(data) {
console.log("第 2 处处理", data);
});
Улучшите свой дизайн с помощью интерфейса Promise
нашappAjax()
Часть интерфейса также может быть разработана как реализация Promise, которая является более общим интерфейсом. Либо используйте функции ES2015+, либо промисы, предоставляемые сторонними библиотеками, такими как jQuery Promises или Bluebird.
function appAjax(action, params) {
// axios 依赖于 Promise,ES5 中可以使用 Bluebird 提供的 Promise
return axios
.post(apiUrl, {
data: $.extend({
action: action
}, params)
})
.then(function(data) {
// 这里调整了判断顺序,会让代码看起来更简洁
if (!data.code) { return data; }
if (!data.message) { throw data; }
alert(data.message);
}, function() {
alert("服务器错误");
});
}
Зато теперь на фронтенде есть инструменты сборки, можно использовать ES2015+ для настройки Babel, можно и TypeScript… Короче, вариантов много, и писать очень удобно. Тогда вам не нужно ограничиваться тем, что поддерживает ES5 при проектировании. Поэтому рассмотрите возможность использования Promise + async/await для реализации
async function appAjax(action, params) {
// axios 依赖于 Promise,ES5 中可以使用 Bluebird 提供的 Promise
const data = await axios
.post(apiUrl, {
data: $.extend({
action: action
}, params)
})
// 这里模拟一个包含错误消息的结果,以便后面统一处理错误
// 这样就不需要用 try ... catch 了
.catch(() => ({ code: -1, message: "服务器错误" }));
if (!data.code) { return data; }
if (!data.message) { throw data; }
alert(data.message);
}
используется в приведенном выше коде
.catch()
избегатьtry ... catch ...
навыки вНикогда не говорите, что обработка ошибок с синтаксисом async/await реализована без try-catchОн упоминается в этом.
Конечно, вызов бизнес-уровня также может использовать async/await (не забудьте написать его в асинхронной функции):
const data = await appAjax("login", {
username: "uname",
password: "passwd"
}).catch(() => {
alert("登录失败");
});
if (data) {
window.location.assign("home");
}
много раз.done()
Модернизация:
const data = await appAjax(url, params);
console.log("第 1 处处理", data);
console.log("第 2 处处理", data);
резюме
В этой статье в качестве примера рассматривается инкапсуляция вызовов Ajax, которая, кажется, говорит об асинхронных вызовах. Но на самом деле я хочу вам рассказать: как инкапсулировать часто используемую функцию, чтобы добиться повторного использования кода и более кратких вызовов; и вопросы, которые необходимо учитывать в процессе инкапсуляции — прямая и обратная совместимость, при выполнении Инкапсуляция инструмента функций, вы должны попытаться избежать привязки к конкретной функции инструмента и приблизиться к общедоступному стандарту - я не знаю, есть ли у вас какой-либо опыт.