Дополнительно
Как мне вернуть ответ с асинхронного вызова?
У меня есть функция foo
, которая делает запрос Ajax. Как я могу вернуть ответ из foo
?
Я попытался вернуть значение из обратного вызова success
, а также назначить ответ локальной переменной внутри функции и вернуть ее, но ни один из этих способов фактически не возвращает ответ.
function foo() {
var result;
$.ajax({
url: '...',
success: function(response) {
result = response;
// return response; // <- I tried that one as well
}
});
return result;
}
var result = foo(); // It always ends up being `undefined`.
5183
36
Проблема
A в Ajax означает асинхронный . Это означает, что отправка запроса (или, скорее, получение ответа) выводится из нормального потока выполнения. В вашем примере
$ .ajax
возвращается немедленно, а следующее утверждение,return result;
, выполняется еще до того, как функция, которую вы передали какsuccess
, была даже вызвана обратная связь.Вот аналогия, которая, как мы надеемся, проясняет разницу между синхронным и асинхронным потоком:
Синхронный
Представьте, что вы звоните другу и просите его найти что-нибудь для вас. Хотя это может занять некоторое время, вы ждете по телефону и смотрите в космос, пока ваш друг не даст вам ответ, который вам нужен.
То же самое происходит, когда вы делаете вызов функции, содержащий «нормальный» код:
Даже если выполнение
findItem
может занять много времени, любой код, следующий заvar item = findItem ();
, должен ждать , пока функция не вернет результат.Асинхронный
Вы снова звоните своему другу по той же причине. Но на этот раз вы говорите ему, что спешите, и он должен перезвонить вам на ваш мобильный телефон. Вы вешаете трубку, выходите из дома и делаете все, что планировали. Как только ваш друг перезвонит вам, вы имеете дело с информацией, которую он вам дал.
Это именно то, что происходит, когда вы делаете запрос Ajax.
Вместо ожидания ответа выполнение продолжается немедленно, и оператор после выполнения вызова Ajax выполняется. Чтобы получить ответ в конце концов, вы предоставляете функцию, которая будет вызвана после получения ответа, обратный вызов (обратите внимание на что-нибудь? перезвонить ?). Любое утверждение, приходящее после этого вызова, выполняется до вызова обратного вызова.
Решение (я)
Охватите асинхронную природу JavaScript! Хотя некоторые асинхронные операции предоставляют синхронные аналоги (как и «Ajax»), их обычно не рекомендуется использовать, особенно в контексте браузера.
Почему это плохо, ты спрашиваешь?
JavaScript запускается в потоке пользовательского интерфейса браузера, и любой длительный процесс блокирует пользовательский интерфейс, что делает его невосприимчивым. Кроме того, существует верхний предел времени выполнения для JavaScript, и браузер спросит пользователя, продолжать выполнение или нет.
Все это действительно плохой пользовательский опыт. Пользователь не сможет определить, все ли работает нормально или нет. Кроме того, эффект будет хуже для пользователей с медленным соединением.
Далее мы рассмотрим три различных решения, которые все строятся друг на друге:
Обещания с
async / await
(ES2017 +, доступны в старых браузерах, если вы используете трансполятор или регенератор)Обратные вызовы (популярны в узле)
Обещания с
then ()
(ES2015 +, доступны в старых браузерах, если вы используете одну из многочисленных библиотек обетований)Все три доступны в текущих браузерах и узле 7+.
ES2017 +: Обещания с
async / await
Версия ECMAScript, выпущенная в 2017 году, представила поддержку на уровне синтаксиса для асинхронных функций. С помощью
async
иawait
вы можете писать асинхронно в «синхронном стиле». Код все еще асинхронный, но его легче читать / понимать.async / await
основывается на обещаниях: функцияasync
всегда возвращает обещание. «приманка» «обменяет» обещание и либо приводит к значению, с которым обещание было решено, либо выдает ошибку, если обещание было отклонено.Важно: Вы можете использовать
await
только внутри функцииasync
. Прямо сейчасawait
верхнего уровня еще не поддерживается, поэтому вам, возможно, придется создать async IIFE (Немедленно вызванное выражение функции), чтобы начать контекстasync
.Вы можете прочитать больше о
async
иavait
на MDN .Вот пример, который основан на верхней части задержки выше:
Текущие версии browser и node поддерживают
async / await
. Вы также можете поддерживать старые среды, преобразуя свой код в ES5 с помощью регенератора (или инструментов, использующих регенератор, таких как Babel).Пусть функции принимают обратные вызовы
Обратный вызов - это просто функция, передаваемая другой функции. Эта другая функция может вызывать переданную функцию, когда она готова. В контексте асинхронного процесса обратный вызов будет вызываться всякий раз, когда выполняется асинхронный процесс. Обычно результат передается в обратный вызов.
В примере вопроса вы можете заставить
foo
принять обратный вызов и использовать его как обратный вызовsuccess
. Так чтостановится
Здесь мы определили функцию «встроенный», но вы можете передать любую ссылку на функцию:
foo
определяется следующим образом:callback
будет ссылаться на функцию, которую мы передаемfoo
, когда мы ее вызываем, и мы просто передаем ееsuccess
. Т.е. как только запрос Ajax будет успешным,$ .ajax
вызоветcallback 'и передаст ответ на обратный вызов (который можно назвать
result`, поскольку именно так мы определили обратный вызов).Вы также можете обработать ответ перед передачей его в обратный вызов:
С помощью обратных вызовов легче писать код, чем может показаться. В конце концов, JavaScript в браузере сильно зависит от событий (события DOM). Получение ответа Ajax - не что иное, как событие.
Трудности могут возникнуть, когда вам приходится работать со сторонним кодом, но большинство проблем можно решить, просто продумав поток приложений.
ES2015 +: Обещания с then ()
Promise API является новой функцией ECMAScript 6 (ES2015), но она уже имеет хорошую [поддержку браузера][10]. Есть также много библиотек, которые реализуют стандартный API Promises и предоставляют дополнительные методы для упрощения использования и составления асинхронных функций (например,. bluebird).
Обещания - это контейнеры для будущих значений. Когда обещание получает значение (оно решено ) или когда оно отменено ( отклонено ), оно уведомляет всех своих «слушателей», которые хотят получить доступ к этому значению.
Преимущество простых обратных вызовов заключается в том, что они позволяют вам разъединять ваш код, и их легче составить.
Вот простой пример использования обещания:
Применительно к нашему вызову Ajax мы могли бы использовать такие обещания:
Описание всех преимуществ, которые обещают предложить, выходит за рамки этого ответа, но если вы пишете новый код, вы должны серьезно рассмотреть их. Они обеспечивают отличную абстракцию и разделение вашего кода.
Больше информации об обещаниях: HTML5 broch - JavaScript Promises
Примечание: отложенные объекты jQuery
Отложенные объекты - это пользовательская реализация обещаний jQuery (до того, как API Promise был стандартизирован). Они ведут себя почти как обещания, но выставляют немного другой API .
Каждый метод Ajax jQuery уже возвращает «отложенный объект» (фактически обещание отложенного объекта), который вы можете просто вернуть из своей функции:
Примечание: Обещание гоча
Имейте в виду, что обещания и отложенные объекты являются просто сдерживающими факторами для будущей ценности, они не являются самой ценностью. Например, предположим, у вас было следующее:
Этот код неправильно понимает вышеуказанные проблемы асинхронности. В частности,
$ .ajax ()
не замораживает код при проверке страницы '/ password' на вашем сервере - он отправляет запрос на сервер и, пока он ожидает, немедленно возвращает объект jQuery Ajax Deferred, а не ответ с сервера. Это означает, что операторif
всегда получит этот Отложенный объект, обработает его какtrue
и будет действовать так, как будто пользователь вошел в систему. Фигово.Но исправить легко:
Не рекомендуется: синхронные вызовы "Ajax"
Как я уже упоминал, некоторые (!) асинхронные операции имеют синхронные аналоги. Я не защищаю их использование, но для полноты, вот как вы бы выполнили синхронный вызов:
Без jQuery
Если вы непосредственно используете объект
XMLHTTPRequest
, передайтеfalse
в качестве третьего аргумента.open
.jQuery
Если вы используете jQuery, вы можете установить для параметра
async
значениеfalse
. Обратите внимание, что эта опция устарела с jQuery 1.8.Затем вы можете либо использовать обратный вызов
success
, либо получить доступ к свойствуresponseText
объекта jqXHR:Если вы используете любой другой метод jQuery Ajax, такой как
$ .get
,$ .getJSON
и т. Д., вы должны изменить его на$ .ajax
(поскольку вы можете передать только параметры конфигурации в$ .ajax
).Загоняет! Невозможно сделать синхронный запрос JSONP. JSONP по своей природе всегда асинхронен (еще одна причина, чтобы даже не рассматривать этот вариант).
[10]: http://caniuse.com/#feat= обещает "caniuse"
Если вы не используете jQuery в своем коде, этот ответ для вас
Ваш код должен быть чем-то вроде этого:
Феликс Клинг отлично поработал, написав ответ для людей, использующих jQuery для AJAX, я решил предоставить альтернативу людям, которые этого не делают.
(Примечание, для тех, кто использует новый API
fetch
, Angular или обещания, я добавил еще один ответ ниже)С чем ты сталкиваешься
Это краткое изложение «Объяснения проблемы» из другого ответа, если вы не уверены, прочитав это, прочитайте это.
A в AJAX означает асинхронный . Это означает, что отправка запроса (или, скорее, получение ответа) выводится из нормального потока выполнения. В вашем примере
.send
возвращается немедленно, и следующее утверждение,return result;
, выполняется до того, как функция, которую вы передали какsuccess
, была даже вызвана обратная связь.Это означает, что когда вы возвращаетесь, указанный вами слушатель еще не выполнил, что означает, что возвращаемое вами значение не было определено.
Вот простая аналогия
[(Скрипка)][2]
Значение
a
returned являетсянеопределенным
, поскольку частьa = 5
еще не выполнена. AJAX действует следующим образом, вы возвращаете значение до того, как сервер получит возможность сообщить вашему браузеру, что это за значение.Одним из возможных решений этой проблемы является кодирование re-active, сообщающее вашей программе, что делать, когда расчет завершен.
Это называется CPS. По сути, мы передаем «getFive» действие, которое нужно выполнить, когда оно завершается, мы рассказываем нашему коду, как реагировать, когда событие завершается (например, наш вызов AJAX или, в данном случае, тайм-аут).
Использование будет:
Который должен предупредить «5» на экран. [(Скрипка)][4].
Возможные решения
Есть в основном два способа, как решить это:
1. Синхронный AJAX - Не делай этого!!
Что касается синхронного AJAX, не делай этого! Ответ Феликса вызывает некоторые убедительные аргументы о том, почему это плохая идея. Подводя итог, можно заморозить браузер пользователя, пока сервер не вернет ответ, и создать очень плохой пользовательский опыт. Вот еще одно краткое резюме, взятое из MDN о том, почему:
Если вы хотите сделать это, вы можете передать флаг: Вот как:
2. Код структуры
Пусть ваша функция принимает обратный вызов. В примере код
foo
может быть сделан, чтобы принять обратный вызов. Мы расскажем нашему коду, как react, когдаfoo
завершит.Так:
Становится:
Здесь мы передали анонимную функцию, но мы могли бы также легко передать ссылку на существующую функцию, сделав ее похожей на:
Для получения более подробной информации о том, как делается этот вид обратного вызова, проверьте ответ Феликса.
Теперь давайте определим самого Фу, чтобы действовать соответственно
[(скрипка)][6]
Теперь мы заставили нашу функцию foo принять действие, которое будет запущено после успешного завершения AJAX, мы можем расширить его, проверив, не равен ли статус ответа 200, и действуя соответствующим образом (создать обработчик сбоев и т. Д.). Эффективно решая нашу проблему.
Если вам все еще трудно понять это прочитайте руководство по началу работы AJAX в MDN .
XMLHttpRequest 2 (прежде всего прочитайте ответы от Бенджамина Грюенбаума и Феликса Клинга)
Если вы не используете jQuery и хотите хороший короткий XMLHttpRequest 2, который работает в современных браузерах, а также в мобильных браузерах, я предлагаю использовать его следующим образом:
Как видите:
Есть два способа получить ответ на этот вызов Ajax (три с использованием имени var XMLHttpRequest):
Самый простой:
Или, если по какой-то причине вы
bind ()
обратный вызов к классу:Пример:
Или (вышеупомянутые лучше анонимные функции всегда проблема):
Нет ничего проще.
Теперь некоторые люди, вероятно, скажут, что лучше использовать onreadystatechange или даже имя переменной XMLHttpRequest. Это неверно.
Проверьте Усовершенствованные функции XMLHttpRequest
Поддерживались все * современные браузеры. И я могу подтвердить, что я использую этот подход, поскольку существует XMLHttpRequest 2. У меня никогда не было проблем со всеми браузерами, которые я использую.
onreadystatechange полезен только в том случае, если вы хотите получить заголовки в состоянии 2.
Использование имени переменной
XMLHttpRequest
является еще одной большой ошибкой, поскольку вам необходимо выполнить обратный вызов внутри замыканий onload / oreadystatechange, если вы его потеряли.Теперь, если вы хотите что-то более сложное, используя post и FormData, вы можете легко расширить эту функцию:
Снова ... это очень короткая функция, но она получает и пост.
Примеры использования:
Или передать элемент полной формы (
document.getElementsByTagName ('form') [0]
):Или установить некоторые пользовательские значения:
Как видите, я не реализовал синхронизацию... это плохо.
Сказав это ... почему бы не сделать это простым способом?
Как упомянуто в комментарии использование ошибки & & синхронный полностью нарушает точку ответа. Это хороший короткий способ использовать Ajax надлежащим образом?
В приведенном выше скрипте у вас есть обработчик ошибок, который статически определен, поэтому он не ставит под угрозу функцию. Обработчик ошибок также может использоваться для других функций.
Но чтобы действительно устранить ошибку, единственный способ - написать неправильный URL, и в этом случае каждый браузер выдает ошибку.
Обработчики ошибок могут быть полезны, если вы установите пользовательские заголовки, установите для reponseType буфер массива BLOB-объектов или что-то еще...
Даже если вы передадите «POSTAPAPAP» как метод, он не вызовет ошибку.
Даже если вы передадите 'fdggdgilfdghfldj' как formdata, это не приведет к ошибке.
В первом случае ошибка находится внутри
displayAjax ()
вthis.statusText
какМетод не разрешен
.Во втором случае это просто работает. Вы должны проверить на стороне сервера, передали ли вы правильные данные сообщения.
кросс-домен не допускается автоматически прикидывает ошибку.
В ответе на ошибку нет кодов ошибок.
Существует только
this.type
, который установлен на ошибку.Зачем добавлять обработчик ошибок, если вы полностью не контролируете ошибки? Большинство ошибок возвращаются внутри этого в функции обратного вызова
displayAjax ()
.Итак: нет необходимости проверять ошибки, если вы можете правильно скопировать и вставить URL. ;)
Не делай этого.
Если вы хотите заблокировать браузер на некоторое время, загрузите большой файл
.txt
синхронно.Теперь вы можете сделать
Нет другого способа сделать это неасинхронным способом. (Да, с циклом setTimeout... но серьезно?)
Еще один момент... если вы работаете с API или просто с файлами вашего собственного списка или с кем-то еще, вы всегда используете разные функции для каждого запроса...
Только если у вас есть страница, на которой вы всегда загружаете один и тот же XML / JSON или что вам нужно, только одна функция. В этом случае немного измените функцию Ajax и замените b своей специальной функцией.
Вышеуказанные функции предназначены для базового использования.
Если вы хотите продлить функцию...
Да, ты можешь.
Я использую много API, и одна из первых функций, которые я интегрирую в каждую HTML-страницу, - это первая функция Ajax в этом ответе, только с GET...
Но вы можете делать много вещей с XMLHttpRequest 2:
Я создал менеджер загрузок (используя диапазоны с обеих сторон с резюме, файлочитателем, файловой системой), различные преобразователи изображений с использованием холста, заполнение веб-баз данных SQL с помощью base64images и многое другое... Но в этих случаях вы должны создать функцию только для этой цели... иногда вам нужен BLOB-объект, буферы массивов, вы можете установить заголовки, переопределить mimetype, и это намного больше...
Но вопрос здесь в том, как вернуть ответ Ajax... (Я добавил простой способ.)
Если вы используете обещания, этот ответ для вас.
Это означает AngularJS, jQuery (с отсрочкой), замену (приемка) нативного XHR, EmberJS, сохранение BackboneJS или любую библиотеку узлов, которая возвращает обещания.
Ваш код должен быть чем-то вроде этого:
Феликс Клинг отлично поработал, написав ответ для людей, использующих jQuery с обратными вызовами для AJAX. У меня есть ответ для родного XHR. Этот ответ предназначен для общего использования обещаний на переднем или заднем плане.
Основная проблема
Модель параллелизма JavaScript в браузере и на сервере с NodeJS / io.js - asynchronous и reactive.
Всякий раз, когда вы вызываете метод, который возвращает обещание, обработчики
then
выполняются always асинхронно - то есть after код под ними, которого нет в обработчике.then
.Это означает, что когда вы возвращаете
data
, определенный вами обработчикthen
еще не выполнен. Это, в свою очередь, означает, что возвращаемое вами значение не было установлено на правильное значение во времени.Вот простая аналогия по этому вопросу: & Лт;!- начать фрагмент: js скрыть: false - >
& Лт;!- конец фрагмента - >
Значение
data
являетсянеопределенным
, поскольку частьdata = 5
еще не выполнена. Вероятно, он будет выполнен через секунду, но к тому времени он не будет иметь отношения к возвращаемому значению.Поскольку операция еще не произошла (AJAX, вызов сервера, IO, таймер), вы возвращаете значение до того, как запрос получил возможность сообщить вашему коду, что это за значение.
Одним из возможных решений этой проблемы является кодирование re-active, сообщающее вашей программе, что делать, когда расчет завершен. Обещания активно позволяют это, будучи временным (чувствительным ко времени) по своей природе.
Быстро подведем итоги обещаний
Обещание - это значение с течением времени. Обещания имеют состояние, они начинаются как ожидающие без значения и могут соглашаться на:
Обещание может изменять только состояния once, после чего оно всегда будет оставаться в одном и том же состоянии навсегда. Вы можете прикрепить обработчики
then
к обещаниям извлечь их ценность и обработать ошибки.then
обработчики разрешают цепь вызовов. Обещания создаются с использованием API, которые их возвращают. Например, более современная замена AJAXfetch
или jQuery$ .get
обещает возврат.Когда мы вызываем
.then
на обещание и return что-то из него - мы получаем обещание для обработанного значения. Если мы вернем еще одно обещание, мы получим удивительные вещи, но давайте держать наших лошадей.С обещаниями
Посмотрим, как мы можем решить вышеуказанный вопрос с помощью обещаний. Во-первых, давайте продемонстрируем наше понимание состояний обещаний сверху, используя Конструктор обещаний для создания функции задержки.:
Теперь, после того, как мы [преобразуем setTimeout] (stackoverflow.com/questions/22519784/how-do-i-convert-an-существующий обратный вызов-api-to-promises) для использования обещаний, мы можем использовать
then
, чтобы сделать это считать:& Лт;!- начать фрагмент: js скрыть: false - >
& Лт;!- конец фрагмента - >
По сути, вместо возврата value, который мы не можем сделать из-за модели параллелизма - мы возвращаем wrapper для значения, которое мы можем unwrap с
then
. Это как коробка, которую вы можете открыть с помощьюthen
.Применять это
Это то же самое для вашего оригинального вызова API, вы можете:
Так что это работает так же хорошо. Мы узнали, что не можем возвращать значения из уже асинхронных вызовов, но мы можем использовать обещания и связывать их для выполнения обработки. Теперь мы знаем, как вернуть ответ с асинхронного вызова.
ES2015 (ES6)
ES6 вводит генераторы, которые являются функциями, которые могут возвращаться посередине, а затем возобновлять точку, в которой они находились. ,. Обычно это полезно для последовательностей, например:
Является функцией, которая возвращает iterator по последовательности
1,2,3,3,3,....
который может быть повторяемым. Хотя это интересно само по себе и открывает пространство для большой возможности, есть один конкретный интересный случай.Если создаваемая нами последовательность действий, а не чисел, мы можем приостановить функцию всякий раз, когда выполняется действие, и подождать ее, прежде чем возобновить функцию. Поэтому вместо последовательности чисел нам нужна последовательность значений future, то есть: обещания.
Этот несколько хитрый, но очень мощный трюк позволяет нам писать асинхронный код синхронно. Есть несколько «бегунов», которые делают это для вас, написание одной из них - это несколько коротких строк кода, но выходит за рамки этого ответа. Я буду использовать здесь «Promise.coroutine» Bluebird, но есть и другие обертки, такие как «co» или «Q.async».
Этот метод возвращает само обещание, которое мы можем потреблять из других корутинов. Например:
ES2016 (ES7)
В ES7 это дополнительно стандартизировано, сейчас есть несколько предложений, но во всех из них вы можете «предоставить» обещание. Это просто «сахар» (более низкий синтаксис) для предложения ES6 выше, добавляя ключевые слова «async» и «await». Делаем приведенный выше пример:
Это все еще возвращает обещание точно так же :)
Вы используете Ajax неправильно. Идея состоит не в том, чтобы что-то возвращать, а вместо этого передавать данные тому, что называется функцией обратного вызова, которая обрабатывает данные.
То есть:
Возврат чего-либо в обработчике отправки ничего не сделает. Вместо этого вы должны либо передать данные, либо делать с ними все, что вы хотите, непосредственно в функции успеха.
Самое простое решение - создать функцию JavaScript и вызвать ее для обратного вызова Ajax
success
.Я отвечу ужасно выглядящим, нарисованным от руки комиксом. Второе изображение является причиной, по которой
result
неопределяется
в вашем примере кода.Angular1
Для людей, которые используют AngularJS, могут справиться с этой ситуацией, используя «Обещания».
Здесь это говорит
Вы также можете найти хорошее объяснение здесь.
Пример найден в docs, упомянутых ниже.
Angular2 и позже
В «Angular2» рассмотрим следующий пример, но его рекомендуется использовать «Observables» с «Angular2».
}
Вы можете потреблять это таким образом
Смотрите оригинал пост здесь. Но Typescript не поддерживает native es6 Promises, если вы хотите использовать его, вам может понадобиться плагин для этого.
Кроме того, здесь есть обещания spec, определяемые здесь.
Большинство ответов здесь дают полезные предложения, когда у вас есть одна операция асинхронной системы, но иногда это возникает, когда вам нужно выполнить асинхронную операцию для каждой записи в массиве или другой структуре, подобной списку. Искушение сделать это:
Пример:
& Лт;!- начать фрагмент: js hide: истинная консоль: истинная павиана: ложная - >
& Лт;!- конец фрагмента - >
Причина, по которой это не работает, заключается в том, что обратные вызовы
doSomethingAsync
еще не запущены к тому времени, когда вы пытаетесь использовать результаты.Итак, если у вас есть массив (или какой-либо список) и вы хотите выполнять операции async для каждой записи, у вас есть два варианта: выполнять операции параллельно (перекрывать) или последовательно (один за другим в последовательности).
Параллельно
Вы можете запустить их все и отслеживать, сколько обратных вызовов вы ожидаете, а затем использовать результаты, когда вы получили столько обратных вызовов:
Пример:
& Лт;!- начать фрагмент: js hide: истинная консоль: истинная павиана: ложная - >
& Лт;!- конец фрагмента - >
Обратите внимание, как мы используем «index» из «forEach», чтобы сохранить результат в «результатах» в том же положении, что и запись, к которой он относится, даже если результаты приходят не по порядку (поскольку вызовы async не обязательно завершаются в порядок, в котором они были начаты).
Но что, если вам нужно вернуть эти результаты из функции? Как уже указывалось в других ответах, вы не можете; Вы должны заставить свою функцию принять и вызвать обратный вызов (или вернуть Обещание). Вот версия обратного вызова:
Пример:
& Лт;!- начать фрагмент: js hide: истинная консоль: истинная павиана: ложная - >
& Лт;!- конец фрагмента - >
Или вот версия, возвращающая «Обещание» вместо:
doSomethingAsync
передало нам ошибки, мы использовали быreject
, чтобы отклонить обещание, когда мы получили ошибку.) *Пример:
& Лт;!- начать фрагмент: js hide: истинная консоль: истинная павиана: ложная - >
& Лт;!- конец фрагмента - >
doSomethingAsync
, которая возвращает обещание, а затем сделать следующее...) *Если
doSomethingAsync
дает вам Обещание, вы можете использоватьPromise.all
:Если вы знаете, что
doSomethingAsync
будет игнорировать второй и третий аргумент, вы можете просто передать его непосредственно вmap
(map
вызывает обратный вызов с тремя аргументами, но большинство людей используют только первый в большинстве случаев): ,Пример:
& Лт;!- начать фрагмент: js hide: истинная консоль: истинная павиана: ложная - >
& Лт;!- конец фрагмента - >
Обратите внимание, что
Promise.all
разрешает свое обещание с помощью множества результатов всех обещаний, которые вы даете ему, когда все они решены, или отклоняет свое обещание, когда первое из обещаний, которые вы даете ему, отклоняет.Серия
Предположим, вы не хотите, чтобы операции были параллельными? Если вы хотите запускать их одну за другой, вам нужно подождать, пока каждая операция не будет завершена, прежде чем начинать следующую. Вот пример функции, которая делает это и вызывает обратный вызов с результатом:
results.push (result)
, поскольку мы знаем, что не получим результаты из строя. В приведенном выше мы могли бы использоватьresults [index] = result;
, но в некоторых из следующих примеров у нас нет индекса для использования.) *Пример:
& Лт;!- начать фрагмент: js hide: истинная консоль: истинная павиана: ложная - >
& Лт;!- конец фрагмента - >
doSomethingAsync
, которая дает вам обещание и делает ниже...) *Если
doSomethingAsync
дает вам обещание, если вы можете использовать синтаксис ES2017+ (возможно, с таким трансполером, как Babel), вы можете использовать функцию `async
сfor-of
иawait
:Пример:
& Лт;!- начать фрагмент: js hide: истинная консоль: истинная павиана: ложная - >
& Лт;!- конец фрагмента - >
Если вы не можете использовать синтаксис ES2017 + (пока), вы можете использовать вариацию шаблона «Обещание уменьшения» (это сложнее, чем обычное уменьшение обещаний, потому что мы не передаем результат из одного в следующий, но вместо этого собирать их результаты в массиве):
Пример:
& Лт;!- начать фрагмент: js hide: истинная консоль: истинная павиана: ложная - >
& Лт;!- конец фрагмента - >
. что менее громоздко с ES2015 + функции стрелки :..
Пример:
& Лт;!- начать фрагмент: js hide: истинная консоль: истинная павиана: ложная - >
& Лт;!- конец фрагмента - >
Посмотрите на этот пример:
Как вы можете видеть,
getJoke
возвращает a dolided обещание (оно разрешается при возвратеres.data.value
). Таким образом, вы ждете, пока не будет выполнен запрос $ http.get , а затем выполняется console.log (res.joke) (как обычный асинхронный поток).Это plnkr:
http://embed.plnkr.co/XlNR7HpCaIhJxskMJfSg/
ES6 путь (асинхронный - жду)
Это одно из мест, где два способа связывания данных или концепция магазина , которые используются во многих новых средах JavaScript, отлично подойдут для вас...
Так что, если вы используете Angular, Реагируйте или любые другие фреймворки, которые связывают данные двумя способами или хранят концепцию , эта проблема просто исправлена для вас, так легко, ваш результат "не определен" на первом этапе, поэтому у вас есть
result = undefined
до получения данных, затем, как только вы получите результат, он будет обновлен и будет присвоен новому значению, которое отвечает на ваш вызов Ajax...Но как вы можете сделать это в чистом javascript или jQuery , например, как вы задали в этом вопросе?
Вы можете использовать callback , обещание и недавно наблюдаемое , чтобы обработать его для вас, например, в обещаниях у нас есть некоторая функция, такая как
success ()
илиthen ()
, которая будет выполняется, когда ваши данные будут готовы для вас, то же самое с функцией обратного вызова или подписки на *.Например, в вашем случае, который вы используете jQuery , вы можете сделать что-то вроде этого:
Для получения дополнительной информации изучите обещания и наблюдателей , которые являются более новыми способами сделать это асинхронным материалом.
Другой подход к возврату значения из асинхронной функции - это передача объекта, который будет хранить результат из асинхронной функции.
Вот пример того же:
Я использую объект
result
для хранения значения во время асинхронной операции. Это позволяет получить результат даже после асинхронного задания.Я часто использую этот подход. Мне было бы интересно узнать, насколько хорошо работает этот подход, когда используется передача результата через последовательные модули.
В то время как обещания и обратные вызовы работают нормально во многих ситуациях, сзади возникает боль, чтобы выразить что-то вроде:
Вы в конечном итоге пройдете через
async1
; проверьте, не определено лиname
или нет, и соответственно вызовите обратный вызов.Хотя это okay в небольших примерах, это раздражает, когда у вас много подобных случаев и обработка ошибок.
«Фиберс» помогает в решении проблемы.
Вы можете оформить проект здесь.
Краткий ответ: Вы должны реализовать обратный вызов, как это:
Следующий пример, который я написал, показывает, как это сделать
Этот рабочий пример является автономным. Он определит простой объект запроса, который использует окно
XMLHttpRequest
для совершения вызовов. Он определит простую функцию ожидания выполнения нескольких обещаний.Контекст. В качестве примера можно запросить конечную точку Spotify Web API, чтобы найти объекты
playlist
для данного набора строк запроса:Для каждого элемента новое обещание запустит блок -
ExecutionBlock
, проанализирует результат, запланирует новый набор обещаний на основе массива результатов, то есть списка объектов «пользователя» Spotify и выполнит новый HTTP-вызов вExecutionProfileBlock
асинхронно.Затем вы можете увидеть вложенную структуру Promise, которая позволяет вам создавать несколько и полностью асинхронных вложенных HTTP-вызовов и объединять результаты каждого подмножества вызовов через
Promise.all
.ПРИМЕЧАНИЕ Недавние API-интерфейсы Spotify
search
потребуют, чтобы в заголовках запросов был указан токен доступа:Итак, вам нужно запустить следующий пример, который вам нужно поместить в маркер доступа в заголовках запросов:
& Лт;!- начать фрагмент: js скрыть: false - >
& Лт;!- конец фрагмента - >
Я широко обсуждал это решение здесь.
Ответ № 2017: теперь вы можете делать именно то, что хотите в каждом текущем браузере и узле
Это довольно просто:
Вот рабочая версия вашего кода
приманка поддерживается во всех текущих браузерах и узле 8
Это очень распространенная проблема, с которой мы сталкиваемся, борясь с «тайнами» JavaScript. Позвольте мне попробовать демистифицировать эту тайну сегодня.
Начнем с простой функции JavaScript:
Это простой синхронный вызов функции (где каждая строка кода «закончилась своей работой» перед следующей последовательностью), и результат такой же, как и ожидалось.
Теперь давайте добавим немного изюминки, введя небольшую задержку в нашу функцию, чтобы все строки кода не были «закончены» последовательно. Таким образом, он будет эмулировать асинхронное поведение функции:
Итак, эта задержка просто нарушила функциональность, которую мы ожидали! Но что именно произошло ? Ну, на самом деле довольно логично, если вы посмотрите на код. функция
foo ()
при выполнении ничего не возвращает (таким образом, возвращаемое значениене определено
), но она запускает таймер, который выполняет функцию после 1 с, чтобы вернуть 'wooho'. Но, как вы можете видеть, значение, которое назначено для бара, - это немедленно возвращаемый материал из foo (), а не что-то еще, что приходит позже.Итак, как мы решаем эту проблему?
Давайте попросим нашу функцию для PROMISE . Promise действительно о том, что это значит: это означает, что функция гарантирует вам любой вывод, который она получит в будущем. так что давайте посмотрим на нашу маленькую проблему выше:
Таким образом, сводка - решать асинхронные функции, такие как вызовы на основе ajax и т. Д.Вы можете использовать обещание «разрешить» значение (которое вы намерены вернуть). Таким образом, короче говоря, вы разрешаете значение вместо returning в асинхронных функциях.
ОБНОВЛЕНИЕ (Обещания с асинком / приманкой)
Помимо использования «тогда / поймать» для работы с обещаниями, существует еще один подход. Идея состоит в том, чтобы распознать асинхронную функцию , а затем подождать, пока обещания не будут решены, прежде чем переходить к следующей строке кода. Это все еще просто «обещания» под капотом, но с другим синтаксическим подходом. Чтобы прояснить ситуацию, вы можете найти сравнение ниже:
тогда / поймать версию:
версия async / await:
Вы можете использовать эту пользовательскую библиотеку (написанную с помощью Promise) для совершения удаленного вызова.
Простой пример использования:
Другое решение - выполнить код через последовательный исполнитель nsynjs.
Если основная функция упрощена
nsynjs оценит все обещания последовательно и поместит результат обещания в свойство
data
:& Лт;!- начать фрагмент: js hide: ложная консоль: истинная павиана: false - >
& Лт;!- конец фрагмента - >
Если базовая функция не упрощена
Шаг 1. Функция обтекания с обратным вызовом в оболочку с поддержкой nsynjs (если она имеет упрощенную версию, вы можете пропустить этот шаг):
Шаг 2. Поместите синхронную логику в функцию:
Шаг 3. Запустите функцию синхронно через nsynjs:
Nsynjs будет оценивать все операторы и выражения шаг за шагом, приостанавливая выполнение в случае, если результат какой-то медленной функции не готов.
Больше примеров здесь: https://github.com/amaksr/nsynjs/tree/master/examples
Браузер можно разделить на три части:
Event Loop
Веб API
Очередь событий
Event Loop работает вечно, то есть своего рода бесконечный цикл. Event Queue - это место, где все ваши функции выдвигаются на какое-то событие(пример: нажмите) это один за другим, выполненный из очереди и помещенный в цикл событий, который выполняет эту функцию и подготавливает ее самостоятельно к следующему после выполнения первой. Это означает, что выполнение одной функции не выполняет 't запускается до тех пор, пока функция до ее выполнения в очереди не будет выполнена в цикле событий.
Теперь давайте подумаем, что мы выдвинули две функции в очереди, одна из которых предназначена для получения данных с сервера, а другая использует эти данные. Сначала мы запустили функцию serverRequest () в очередь, а затем использовали функцию Data (). Функция serverRequest входит в цикл событий и выполняет вызов на сервер, поскольку мы никогда не знаем, сколько времени потребуется для получения данных с сервера так что этот процесс, как ожидается, займет время, и поэтому мы заняты нашей петлей событий, таким образом, вешая нашу страницу, это 'Если веб-API вступает в роль, он берет эту функцию из цикла событий и имеет дело с сервером, делающим цикл событий свободным, чтобы мы могли выполнять следующую функцию из очереди. Следующая функция в очереди - utiliseData( который идет в ногу, но из-за отсутствия доступных данных он теряется, и выполнение следующей функции продолжается до конца очереди.(Это называется вызовом Async, то есть мы можем сделать что-то еще, пока не получим данные)
Предположим, что наша функция serverRequest () имела оператор возврата в коде, когда мы вернем данные с сервера, веб-API вытолкнет их в очередь в конце очереди. Поскольку его толкают в конце очереди, мы не можем использовать его данные, поскольку в нашей очереди не осталось функции для использования этих данных. Таким образом, невозможно вернуть что-то из Async Call.
Таким образом, решение для этого - обратный вызов или обещание .
В моем Кодексе это называется как
Читайте здесь о новых методах в ECMA (2016/17) для выполнения асинхронного вызова (@Felix Kling Answer on Top) https://stackoverflow.com/a/14220323/7579856