Как я могу заставить jQuery выполнять синхронный, а не асинхронный Ajax-запрос?
-
02-07-2019 - |
Вопрос
У меня есть виджет JavaScript, который предоставляет стандартные точки расширения.Одним из них является beforecreate
функция.Он должен вернуться false
чтобы предотвратить создание элемента.
Я добавил вызов Ajax в эту функцию с помощью jQuery:
beforecreate: function (node, targetNode, type, to) {
jQuery.get('http://example.com/catalog/create/' + targetNode.id + '?name=' + encode(to.inp[0].value),
function (result) {
if (result.isOk == false)
alert(result.message);
});
}
Но я хочу запретить моему виджету создавать элемент, поэтому я должен вернуть false
в материнской функции, а не в обратном вызове.Есть ли способ выполнить синхронный AJAX-запрос с помощью jQuery или любого другого встроенного в браузер API?
Решение
Из самого Документация по jQuery:вы указываете асинхронный возможность быть ложь чтобы получить синхронный Ajax-запрос.Затем ваш обратный вызов может установить некоторые данные до того, как ваша материнская функция продолжит работу.
Вот как выглядел бы ваш код, если бы его изменили, как предложено:
beforecreate: function (node, targetNode, type, to) {
jQuery.ajax({
url: 'http://example.com/catalog/create/' + targetNode.id + '?name=' + encode(to.inp[0].value),
success: function (result) {
if (result.isOk == false) alert(result.message);
},
async: false
});
}
Другие советы
Вы можете перевести настройку Ajax jQuery в синхронный режим, вызвав
jQuery.ajaxSetup({async:false});
А затем выполняйте свои Ajax-вызовы с помощью jQuery.get( ... );
Затем просто включите его еще раз один раз
jQuery.ajaxSetup({async:true});
Я предполагаю, что это работает так же, как предложил @Adam, но это может быть полезно кому-то, кто хочет перенастроить свой jQuery.get()
или jQuery.post()
к более сложным jQuery.ajax()
синтаксис.
Отличное решение!Я заметил, когда пытался это реализовать, что если я возвращал значение в предложении success, оно возвращалось как неопределенное.Мне пришлось сохранить его в переменной и вернуть эту переменную.Это метод, который я придумал:
function getWhatever() {
// strUrl is whatever URL you need to call
var strUrl = "", strReturn = "";
jQuery.ajax({
url: strUrl,
success: function(html) {
strReturn = html;
},
async:false
});
return strReturn;
}
Во всех этих ответах упускается из виду тот факт, что выполнение Ajax-вызова с помощью async:false приведет к зависанию браузера до завершения Ajax-запроса.Использование библиотеки управления потоком позволит решить эту проблему без зависания браузера.Вот пример с Frame.js:
beforecreate: function(node,targetNode,type,to) {
Frame(function(next)){
jQuery.get('http://example.com/catalog/create/', next);
});
Frame(function(next, response)){
alert(response);
next();
});
Frame.init();
}
function getURL(url){
return $.ajax({
type: "GET",
url: url,
cache: false,
async: false
}).responseText;
}
//example use
var msg=getURL("message.php");
alert(msg);
Имейте в виду, что если вы выполняете междоменный вызов Ajax (используя JSONP) - ты не могу делайте это синхронно, чтобы async
флаг будет проигнорирован jQuery.
$.ajax({
url: "testserver.php",
dataType: 'jsonp', // jsonp
async: false //IGNORED!!
});
Для JSONP-вызовов вы могли бы использовать:
- Ajax-вызовите свой собственный домен - и выполните междоменный вызов на стороне сервера
- Измените свой код, чтобы он работал асинхронно
- Используйте библиотеку "function sequencer", подобную Frame.js (это ответ)
- Блокируйте пользовательский интерфейс вместо блокирования выполнения (это ответ) (мой любимый способ)
Примечание:Вам не следует использовать async: false
из-за этого появляются предупреждающие сообщения:
Начиная с версии Gecko 30.0 (Firefox 30.0 / Thunderbird 30.0 / SeaMonkey 2.27), синхронные запросы в основном потоке устарели из-за негативного влияния на работу пользователя.
Chrome даже предупреждает об этом в консоли:
Синхронный XMLHttpRequest в основном потоке устарел из-за его пагубного воздействия на работу конечного пользователя.Для получения дополнительной информации проверьте https://xhr.spec.whatwg.org/.
Это может привести к поломке вашей страницы, если вы делаете что-то подобное, поскольку она может перестать работать в любой день.
Если вы хотите сделать это так, чтобы все еще казалось, что это синхронно, но все еще не блокируется, тогда вам следует использовать async / await и, вероятно, также некоторый ajax, основанный на обещаниях, таких как new Принести API
async function foo() {
var res = await fetch(url)
console.log(res.ok)
var json = await res.json()
console.log(json)
}
Редактировать chrome работает над Запрещение синхронизации XHR при закрытии страницы когда пользователь перемещается по странице или закрывает ее.Это включает в себя beforeunload, unload, pagehide и visibilitychange.
если это ваш вариант использования, то вы, возможно, захотите взглянуть на навигатор.Отправить сообщение вместо этого
Также на странице возможно отключить синхронизацию запроса либо с заголовками http, либо с атрибутом allow iframe
С async: false
вы получаете заблокированный браузер.Для неблокирующего синхронного решения вы можете использовать следующее:
ES6/ECMAScript2015
С ES6 вы можете использовать генератор и библиотека co:
beforecreate: function (node, targetNode, type, to) {
co(function*(){
let result = yield jQuery.get('http://example.com/catalog/create/' + targetNode.id + '?name=' + encode(to.inp[0].value));
//Just use the result here
});
}
ES7
С ES7 вы можете просто использовать asyc await:
beforecreate: function (node, targetNode, type, to) {
(async function(){
let result = await jQuery.get('http://example.com/catalog/create/' + targetNode.id + '?name=' + encode(to.inp[0].value));
//Just use the result here
})();
}
Я использовал ответ, данный Carcione, и изменил его, чтобы использовать JSON.
function getUrlJsonSync(url){
var jqxhr = $.ajax({
type: "GET",
url: url,
dataType: 'json',
cache: false,
async: false
});
// 'async' has to be 'false' for this to work
var response = {valid: jqxhr.statusText, data: jqxhr.responseJSON};
return response;
}
function testGetUrlJsonSync()
{
var reply = getUrlJsonSync("myurl");
if (reply.valid == 'OK')
{
console.dir(reply.data);
}
else
{
alert('not valid');
}
}
Я добавил следующее Тип данных из 'JSON' и изменил .Текст ответа Для Ответјсон.
Я также восстановил статус, используя statusText ( Текст состояния ) свойство возвращаемого объекта.Обратите внимание, что это статус Ajax-ответа, а не то, является ли JSON допустимым.
Серверная часть должна вернуть ответ в правильном (хорошо сформированном) формате JSON, в противном случае возвращаемый объект будет неопределенным.
Есть два аспекта, которые следует учитывать при ответе на первоначальный вопрос.Один из них предписывает Ajax выполнять синхронную работу (установив асинхронный:ложь) а другой возвращает ответ через оператор return вызывающей функции, а не в функцию обратного вызова.
Я также попробовал это с POST, и это сработало.
Я изменил GET на POST и добавил данные:постданные
function postUrlJsonSync(url, postdata){
var jqxhr = $.ajax({
type: "POST",
url: url,
data: postdata,
dataType: 'json',
cache: false,
async: false
});
// 'async' has to be 'false' for this to work
var response = {valid: jqxhr.statusText, data: jqxhr.responseJSON};
return response;
}
Обратите внимание, что приведенный выше код работает только в том случае, когда асинхронный является ложь.Если бы вы должны были установить асинхронный:верно возвращенный объект jqxhr был бы недействителен в момент возврата AJAX-вызова, только позже, когда асинхронный вызов завершится, но уже слишком поздно устанавливать ответ переменная.
Это пример:
$.ajax({
url: "test.html",
async: false
}).done(function(data) {
// Todo something..
}).fail(function(xhr) {
// Todo something..
});
Во-первых, мы должны понимать, когда мы используем $.ajax и когда мы используем $.get / $.post
Когда нам требуется низкоуровневый контроль над ajax-запросом, такой как настройки заголовка запроса, настройки кэширования, синхронные настройки и т.д., тогда мы должны использовать $.ajax.
$.получить/$.post:Когда нам не требуется низкоуровневый контроль над ajax-запросом.Только простое получение / отправка данных на сервер.Это сокращение от
$.ajax({
url: url,
data: data,
success: success,
dataType: dataType
});
и, следовательно, мы не можем использовать другие функции (синхронизацию, кэширование и т.д.) С $.get / $.post.
Следовательно, для низкоуровневого контроля (синхронизация, кэширование и т.д.) Над ajax-запросом мы должны использовать $.ajax
$.ajax({
type: 'GET',
url: url,
data: data,
success: success,
dataType: dataType,
async:false
});
это моя простая реализация для асинхронных запросов с помощью jQuery.Я надеюсь, что это кому-нибудь поможет.
var queueUrlsForRemove = [
'http://dev-myurl.com/image/1',
'http://dev-myurl.com/image/2',
'http://dev-myurl.com/image/3',
];
var queueImagesDelete = function(){
deleteImage( queueUrlsForRemove.splice(0,1), function(){
if (queueUrlsForRemove.length > 0) {
queueImagesDelete();
}
});
}
var deleteImage = function(url, callback) {
$.ajax({
url: url,
method: 'DELETE'
}).done(function(response){
typeof(callback) == 'function' ? callback(response) : null;
});
}
queueImagesDelete();
Потому что XMLHttpReponse
синхронная работа устарела, я придумал следующее решение, которое оборачивает XMLHttpRequest
.Это позволяет упорядочивать AJAX-запросы, оставаясь при этом асинхронными по своей природе, что очень полезно для одноразовых токенов CSRF.
Он также прозрачен, поэтому библиотеки, такие как jQuery, будут работать без проблем.
/* wrap XMLHttpRequest for synchronous operation */
var XHRQueue = [];
var _XMLHttpRequest = XMLHttpRequest;
XMLHttpRequest = function()
{
var xhr = new _XMLHttpRequest();
var _send = xhr.send;
xhr.send = function()
{
/* queue the request, and if it's the first, process it */
XHRQueue.push([this, arguments]);
if (XHRQueue.length == 1)
this.processQueue();
};
xhr.processQueue = function()
{
var call = XHRQueue[0];
var xhr = call[0];
var args = call[1];
/* you could also set a CSRF token header here */
/* send the request */
_send.apply(xhr, args);
};
xhr.addEventListener('load', function(e)
{
/* you could also retrieve a CSRF token header here */
/* remove the completed request and if there is more, trigger the next */
XHRQueue.shift();
if (XHRQueue.length)
this.processQueue();
});
return xhr;
};
Это мой “Нитки” пространство имен:
var Thread = {
sleep: function(ms) {
var start = Date.now();
while (true) {
var clock = (Date.now() - start);
if (clock >= ms) break;
}
}
};
И вот как я это называю:
var d1 = new Date();
console.log('start ' + d1.toLocaleTimeString());
Thread.sleep(10000);
var d2 = new Date();
console.log('end ' + d2.toLocaleTimeString());
И если посмотреть на консоль, то мы получим следующий результат:
start 1:41:21 PM
end 1:41:31 PM