#javascript #jquery #ajax #async-await #promise
Вопрос:
Я должен подключиться к существующему ajax-запросу (который я не могу изменить, он находится внутри нашей CMS) и добавить некоторые дополнительные данные. Если быть более точным, это медиатека, загружающая данные изображений (json), и я добавляю еще несколько изображений из внешнего источника.
var promise_waiting = []; jQuery.ajaxSetup({ beforeSend: function() { // starting external library request, so they can run in parallel promise_waiting = new Promise( function(resolve, reject) { jQuery.get(external_library_url, {}, function(data) { if(data) { console.log('external library request done'); resolve(data); } }); }); return true; }, dataFilter: async function (data) { console.log('original request done'); // waiting for that external library request to resolve ext_data = await promise_waiting; console.log('both requests done'); return data.concat(ext_data); } })
Оба запроса заканчиваются так, как я хочу, так что все работает нормально. Проблема в том, что dataFilter
не может быть асинхронной функцией, нет ошибок или предупреждений, но код, который будет выполнен после запроса (отображение предварительного просмотра изображения), никогда не запускается.
В настоящее время моей единственной альтернативой является использование неасинхронного ajax-запроса dataFilter
, что не является оптимальным, поскольку оба запроса по отдельности занимают довольно много времени.
Я ищу альтернативы этой настройке, заранее благодарю вас
Ответ №1:
Без возможности изменить $.ajax()
вызовы и с beforeSend
amp; dataFilter
, не допускающим асинхронности, вам нужно будет выполнять асинхронность beforeSend
вручную, и я не думаю, что вы сможете избежать необходимости «перехватывать» jQuery.fn.ajax
.
Что-то вроде этого, может быть:-
function myBeforeSend(data) { return $.get(external_library_url, {}) .then(ext_data =gt; data.concat(ext_data || [])); } (function($, beforeSend) { // i.i.f.e. // make an alias for $.fn.ajax let $.fn.originalAjax = $.fn.ajax; // now hijack $.fn.ajax (in a good way) $.fn.ajax = function(...args) { // allow for $.ajax's flexible args .... $.ajax(url, [settings]) or $.ajax([settings]) let settings = Object.assign({}, args[1] || {}); if (typeof args[0] === "string") { settings.url = args[0]; // standardise settings to include url. } return beforeSend(settings.data || []) .then(filteredData =gt; $.originalAjax(Object.assign(settings, { 'data':filteredData }))); }; })(jQuery, myBeforeSend);
В составе довольно много безопасности args
, но, пожалуйста, не забудьте тщательно протестировать.
Комментарии:
1. Я понимаю, что вы пытаетесь здесь сделать, но мне нужен результат исходного запроса ajax для слияния с моими данными ext_data. Кроме того, угон ajax сломал некоторые другие вещи, к сожалению
2. @devssc, «нужен результат исходного запроса ajax для слияния с моими данными ext_data» … за исключением ошибки с моей стороны, это именно то, как он должен себя вести.
beforeSend()
делает первоначальный запрос и возвращает обещание, которое доставляет объединенные данные, которые, в свою очередь, передаются второму запросу ajax.3. Упс, одна крошечная ошибка,
args[2]
должна бытьargs[1]
(отредактирована).4. Проблема с другими нарушениями может быть связана с
settings.data || []
выражением, которое по умолчанию имеет значение пустой массив. Возможно, было бы лучше по умолчанию использовать пустой простой объект{}
, хотя для этого потребуетсяbeforeSend()
различать массив и простой объект.