Как вернуть ответ от асинхронного вызова?

30

javascript,ajax,asynchronous,ecmascript-6,ecmascript-2017,

JavaScript, Ajax, асинхронные, ECMAScript-6, ECMAScript-2017,

Ответов: 4437


4766 ов принято
+150

return result;Для более общего объяснения поведения успеха с dfunction findItem () {var item; while (item_not_found) {// search} return item; } var item = findItem (); // Делаем что-то с элементом doSomethingElse (); см. раздел Почему моя переменная неизменна после того, как я изменяю ее внутри функции? - findItemhronous code reference

var item = findItem(); Если вы уже понимаете проблему, перейдите к возможным решениям ниже.

Проблема

В Ajax означает асинхронный / awaithronous . Это означает, что отправка запроса (или, скорее, получение ответа) вынимается из обычного потока выполнения. В вашем примере возвращается немедленно и следующий оператор ,, выполняется до того, как функция, которую вы передали, так как ожидание было даже вызвано.then()async/awaitasync

Вот аналогия, которая, мы надеемся, делает разницу между синхронным и асинхронным / awaithronous flow clearer:

синхронный

Представьте, что вы позвоните другу и попросите его посмотреть что-нибудь для вас. Хотя это может занять некоторое время, вы ждете по телефону и смотрите в космос, пока ваш друг не даст вам ответ, который вам нужен.

То же самое происходит, когда вы вызываете вызов функции, содержащий «нормальный» код:

async

Несмотря на то, что это awaitможет занять много времени, любой следующий код awaitдолжен ждать, пока функция вернет асинхронный вызов.

асинхронный

Вы снова звоните своему другу по той же причине. Но на этот раз ты скажешь ему, что ты спешишь, и он должен перезвонить тебе на свой мобильный телефон. Вы вешаете трубку, выходите из дома и делаете все, что планируете делать. Как только ваш друг перезвонит вам, вы имеете дело с информацией, которую он вам дал.

Это именно то, что происходит, когда вы делаете запрос Ajax.

await

Вместо того, чтобы ждать ответа, выполнение продолжается немедленно, и оператор после вызова Ajax выполняется. Чтобы получить ответ в конце концов, вы предоставляете функцию, которая будет вызываться после получения ответа, обратного вызова (обратите внимание на что-то? Обратный вызов ?). Любое утверждение, поступившее после этого вызова, выполняется до вызова обратного вызова.


Решение (s)

Объявите асинхронный характер JavaScript! Хотя определенные асинхронные операции предоставляют синхронные копии (так же как и Ajax), обычно их не рекомендуется использовать, особенно в контексте браузера.

Почему ты плохо спрашиваешь?

JavaScript работает в потоке пользовательского интерфейса браузера, и любой длительный процесс блокирует пользовательский интерфейс, делая его невосприимчивым. Кроме того, существует верхний предел времени выполнения JavaScript, и браузер запрашивает у пользователя, продолжать ли это выполнение или нет.

Все это очень плохой пользовательский интерфейс. Пользователь не сможет сказать, все ли работает нормально или нет. Кроме того, эффект будет хуже для пользователей с медленным подключением.

Ниже мы рассмотрим три разных решения, которые все строятся друг над другом:

  • Обещает с// Using 'superagent' which will return a promise. var superagent = require('superagent') // This is isn't declared as `async` because it already returns a promise function delay() { // `delay` returns a promise return new Promise(function(resolve, reject) { // Only `delay` is able to resolve or reject the promise setTimeout(function() { resolve(42); // After 3 seconds, resolve the promise with value 42 }, 3000); }); } async function getAllBooks() { try { // GET a list of book IDs of the current user var bookIDs = await superagent.get('/user/books'); // wait for 3 seconds (just for the sake of this example) await delay(); // GET information about each book return await superagent.get('/books/ids='+JSON.stringify(bookIDs)); } catch(error) { // If any of the awaited promises was rejected, this catch block // would catch the rejection reason return null; } } // Async functions always return a promise getAllBooks() .then(function(books) { console.log(books); }); (ES2017 +, доступный в старых браузерах, если вы используете транспилер или регенератор)
  • Обратные вызовы (популярные в узле)
  • Обещаетasync/await (ES2015 +, доступный в старых браузерах, если вы используете одну из многих библиотек обещаний)

Все три доступны в текущих браузерах, а узел 7+.


ES2017 +: Обещает с foo

В новой версии ECMAScript, выпущенной в 2017 году, появилась поддержка синтаксического уровня для асинхронных функций. С помощью asyncи successвы можете писать асинхронно в «синхронном стиле». Не ошибитесь: код по-прежнему асинхронен, но его легче читать / понимать.

var result = foo(); // Code that depends on 'result' основывается на обещаниях: asyncфункция всегда возвращает обещание. foo(function(result) { // Code that depends on 'result' }); «разворачивает» обещание и либо приводит к тому, что обещание было разрешено, либо порождает ошибку, если обещание было отклонено.

Важно: вы можете использовать только function myCallback(result) { // Code that depends on 'result' } foo(myCallback); внутри asyncфункции. Это означает, что на самом верхнем уровне вам все равно придется работать непосредственно с обещанием.

Вы можете узнать больше о MDN asyncи function foo(callback) { $.ajax({ // ... success: callback }); } о нем.

Вот пример, который основывается на верхней задержке выше:

callback

Поддержка новых браузеров и узловfoo . Вы также можете поддерживать устаревшие среды, преобразовывая свой код в ES5 с помощью регенератора (или инструментов, которые используют регенератор, например, Babel ).


Пусть функции принимают обратные вызовы

Обратный вызов - это просто функция, переданная другой функции. Эта другая функция может вызывать функцию, передаваемую всякий раз, когда она будет готова. В контексте асинхронного процесса обратный вызов будет вызываться всякий раз, когда выполняется асинхронный процесс. Обычно результат передается на обратный вызов.

В примере вопроса вы можете successпринять обратный вызов и использовать его в качестве $.ajaxобратного вызова. Итак, это

callback

становится

result

Здесь мы определили функцию «inline», но вы можете передать любую ссылку на функцию:

function foo(callback) {
    $.ajax({
        // ...
        success: function(response) {
            // For example, filter the response
            callback(filtered_response);
        }
    });
}

function delay() { // `delay` returns a promise return new Promise(function(resolve, reject) { // Only `delay` is able to resolve or reject the promise setTimeout(function() { resolve(42); // After 3 seconds, resolve the promise with value 42 }, 3000); }); } delay() .then(function(v) { // `delay` returns a promise console.log(v); // Log the value once it is resolved }) .catch(function(v) { // Or do something else if it is rejected // (it would not happen in this example, since `reject` is not called). }); сам определяется следующим образом:

function ajax(url) {
  return new Promise(function(resolve, reject) {
    var xhr = new XMLHttpRequest();
    xhr.onload = function() {
      resolve(this.responseText);
    };
    xhr.onerror = reject;
    xhr.open('GET', url);
    xhr.send();
  });
}

ajax("/echo/json")
  .then(function(result) {
    // Code depending on result
  })
  .catch(function() {
    // An error occurred
  });

callbackбудет ссылаться на функцию, к которой мы переходим, function ajax() { return $.ajax(...); } ajax().done(function(result) { // Code depending on result }).fail(function() { // An error occurred }); когда мы ее называем, и мы просто передаем ее function checkPassword() { return $.ajax({ url: '/password', data: { username: $('#username').val(), password: $('#password').val() }, type: 'POST', dataType: 'json' }); } if (checkPassword()) { // Tell the user they're logged in } . Т.е., когда запрос Ajax равен $ .ajax (), ifвызовет callbackи передаст ответ на обратный вызов (на который можно ссылаться result, поскольку именно так мы определили обратный вызов).

Вы также можете обработать ответ, прежде чем передавать его на обратный вызов:

true

Легче писать код с помощью обратных вызовов, чем может показаться. В конце концов, JavaScript в браузере сильно управляется событиями (события DOM). Получение ответа Ajax - это не что иное, как событие.
Трудности могут возникнуть, когда вам придется работать со сторонним кодом, но большинство проблем можно решить, просто продумав поток приложений.


ES2015 +: Обещает с помощью checkPassword () .done (function (r) {if (r) {// Сообщите пользователю, что они вошли в систему} else {// Сообщите пользователю, что у них плохой пароль}}) .fail (function ( x) {// Расскажите, что произошло что-то плохое}); ()

Promise API является новой функцией ECMAScript 6 (ES2015), но он имеет хорошую поддержку браузера уже. Существует также множество библиотек, которые реализуют стандартный API Promises и предоставляют дополнительные методы для облегчения использования и составления асинхронных функций (например, bluebird ).

Обещания - это контейнеры для будущих значений. Когда обещание получает значение (оно разрешено ) или когда оно отменено ( отклонено ), оно уведомляет всех своих «слушателей», которые хотят получить доступ к этому значению.

Преимущество перед обычными обратными вызовами заключается в том, что они позволяют вам развязывать код, и их легче создавать.

Вот простой пример использования обещания:

XMLHTTPRequest

Применительно к нашему призыву Ajax мы могли бы использовать такие обещания:

false

Описывая все преимущества, которые предлагают обещания, выходит за рамки этого ответа, но если вы пишете новый код, вам следует серьезно их рассмотреть. Они обеспечивают отличную абстракцию и разделение вашего кода.

Дополнительная информация о обещаниях: камни HTML5 - JavaScript Promises

Сторона примечания: отложенные объекты jQuery

Отложенные объекты представляют собой пользовательскую реализацию обещаний jQuery (до того, как API Promise был стандартизован). Они ведут себя почти как обещания, но демонстрируют несколько иной API.

Каждый метод Ajax jQuery уже возвращает «отложенный объект» (фактически обещание отложенного объекта), который вы можете просто вернуть из своей функции:

.open

Сторона примечания: Promise gotchas

Имейте в виду, что обещания и отложенные объекты - это просто контейнеры для будущего значения, они не являются самим значением. Например, предположим, что у вас было следующее:

async

Этот код неправильно понимает вышеупомянутые проблемы асинхронности. В частности, falseон не блокирует код при проверке страницы «/ password» на вашем сервере - он отправляет запрос на сервер и, пока он ждет, немедленно возвращает объект JQuery Ajax Deferred, а не ответ с сервера. Это означает, что ifоператор всегда будет получать этот объект «Отложен», рассматривать его как successи продолжать действовать, как если бы пользователь вошел в систему. Не хорошо.

Но исправление легко:

responseText

Не рекомендуется: Синхронные вызовы «Ajax»

Как я уже упоминал, некоторые (!) Асинхронные операции имеют синхронные копии. Я не сторонник их использования, но для полноты, вот как вы могли бы выполнить синхронный вызов:

Без jQuery

Если вы напрямую используете $.getобъект, передайте его в $.getJSONкачестве третьего аргумента $.ajax.

JQuery

Если вы используете jQuery , вы можете установить этот asyncпараметр $.ajax. Обратите внимание, что этот параметр устарел после jQuery 1.8. Вы можете использовать foo () {var httpRequest = new XMLHttpRequest (); httpRequest.open ('GET', "/ echo / json"); httpRequest.send (); return httpRequest.responseText; } var result = foo (); // всегда заканчивается тем, что «undefined» либо использует fetchобратный вызов, либо получает доступ к .sendсвойству объекта jqXHR :

return result;

Если вы используете любой другой метод jQuery Ajax, такой как success, function getFive(){ var a; setTimeout(function(){ a=5; },10); return a; } и т. Д., Вы должны его изменить a(поскольку вы можете передавать только параметры конфигурации undefined).

Берегись! Невозможно выполнить синхронный запрос JSONP . JSONP по самой своей природе всегда асинхронен (еще одна причина не учитывать этот вариант).


904

Если вы не используете jQuery в своем коде, этот ответ для вас

Ваш код должен быть чем-то вроде этого:

a=5

Феликс Клинг отлично справился с написанием ответа для людей, использующих jQuery для AJAX, я решил предоставить альтернативу людям, которые этого не делают.

( Обратите внимание, что для тех, кто использует новый function onComplete(a){ // When the code completes, do this alert(a); } function getFive(whenDone){ var a; setTimeout(function(){ a=5; whenDone(a); },10); } API, Угловые или обещания, я добавил еще один ответ ниже )


То, с чем вы сталкиваетесь

Это краткое резюме «Объяснение проблемы» из другого ответа, если вы не уверены, прочитав это, прочитайте это.

В AJAX означает асинхронный . Это означает, что отправка запроса (или, скорее, получение ответа) вынимается из обычного потока выполнения. В вашем примере возвращается немедленно и следующий оператор ,, выполняется до того, как функция, которую вы передали, была вызвана.getFivegetFive(onComplete); success

Это означает, что когда вы возвращаетесь, слушатель, который вы определили, еще не выполнил, что означает, что возвращаемое вами значение не было определено.

Вот простая аналогия

var request = new XMLHttpRequest();
request.open('GET', 'yourURL', false);  // `false` makes the request synchronous
request.send(null);

if (request.status === 200) {// That's HTTP for 'ok'
  console.log(request.responseText);
}

(Fiddle)

Значение aвозвращается, fooтак как fooчасть еще не выполнена. AJAX действует так, вы возвращаете значение, прежде чем сервер получит возможность сообщить вашему браузеру, что это за значение.

Одним из возможных решений этой проблемы является повторное использование кода , информирование вашей программы о том, что делать, когда расчет завершен.

var result = foo();
// code that depends on `result` goes here

Это называется CPS . В основном, мы передаем foo(function(result) { // code that depends on `result` }); действие для выполнения, когда оно завершается, мы сообщаем нашему кодексу, как реагировать, когда событие завершается (например, наш вызов AJAX или в этом случае тайм-аут).

Использование:

function myHandler(result) {
    // code that depends on `result`
}
foo(myHandler);

Который должен предупредить «5» на экране. (Fiddle) .

Возможные решения

В основном есть два способа решения этой проблемы:

  1. Сделайте синхронный вызов AJAX (позвоните ему SJAX).
  2. Перестройте свой код для правильной работы с обратными вызовами.

1. Синхронный AJAX - Не делай этого!

Что касается синхронного AJAX, не делайте этого! Ответ Феликса вызывает некоторые веские аргументы в пользу того, почему это плохая идея. Подводя итог, он заморозит браузер пользователя, пока сервер не вернет ответ и не создаст очень плохой пользовательский интерфейс. Вот еще одно краткое изложение MDN о том, почему:

XMLHttpRequest поддерживает синхронную и асинхронную связь. В общем, однако, асинхронные запросы должны быть предпочтительнее синхронных запросов по соображениям производительности.

Короче говоря, синхронные запросы блокируют выполнение кода ... ... это может вызвать серьезные проблемы ...

Если вам нужно это сделать, вы можете передать флаг: Вот как это сделать:

function foo(callback) {
    var httpRequest = new XMLHttpRequest();
    httpRequest.onload = function(){ // when the request is loaded
       callback(httpRequest.responseText);// we're calling our method
    };
    httpRequest.open('GET', "/echo/json");
    httpRequest.send();
}

2. Код реструктуризации

Пусть ваша функция принимает обратный вызов. В примере код function ajax(a, b, c){ // URL, callback, just a placeholder c = new XMLHttpRequest; c.open('GET', a); c.onload = b; c.send() } может быть выполнен для принятия обратного вызова. Мы расскажем нашему кодексу, как реагировать, когда this.response закончите.

Так:

bind()

становится:

e.target.response

Здесь мы передали анонимную функцию, но мы могли бы просто передать ссылку на существующую функцию, сделав ее похожей:

function callback(e){
  console.log(this.response);
}
ajax('URL', callback);

Для получения более подробной информации о том, как выполняется этот вид обратного вызова, проверьте ответ Felix.

Теперь давайте определим foo сам, чтобы действовать соответственно

ajax('URL', function(e){console.log(this.response)});

(Скрипка)

Теперь мы сделали нашу функцию foo принятой для запуска, когда AJAX завершил успешную работу, мы можем продолжить это, проверив, не отвечает ли статус ответа 200 и действует соответственно (создайте обработчик ошибок и т. Д.). Эффективное решение нашей проблемы.

Если вам все еще трудно понять это, прочитайте руководство по началу работы AJAX в MDN.


312 ов

XMLHttpRequest 2 (прежде всего, прочитайте ответы Бенджамина Груэнбаума и Феликса Клинга)

Если вы не используете jQuery и хотите иметь красивый короткий XMLHttpRequest 2, который работает в современных браузерах, а также в мобильных браузерах, я предлагаю использовать его так:

XMLHttpRequest

Как вы видете:

  1. Он короче всех остальных функций.
  2. Обратный вызов устанавливается напрямую (так что лишние ненужные закрытия).
  3. Он использует новую onload (так что вам не нужно проверять статус readystate &&)
  4. Есть некоторые другие ситуации, которые я не помню, что делает XMLHttpRequest 1 раздражающим.

Существует два способа получить ответ этого вызова Ajax (три с использованием имени XMLHttpRequest var):

Простейший:

function x(a, b, e, d, c){ // URL, callback, method, formdata or {key:val},placeholder
  c = new XMLHttpRequest;
  c.open(e||'get', a);
  c.onload = b;
  c.send(d||null)
}

Или, если по какой-то причине вы x(url, callback); // By default it's get so no need to set x(url, callback, 'post', {'key': 'val'}); // No need to set post data ответили на класс:

document.getElementsByTagName('form')[0]

Пример:

var fd = new FormData(form);
x(url, callback, 'post', fd);

Или (выше, лучше анонимные функции всегда являются проблемой):

var fd = new FormData();
fd.append('key', 'val')
x(url, callback, 'post', fd);

Ничего проще.

Теперь некоторые люди, вероятно, скажут, что лучше использовать onreadystatechange или даже имя переменной XMLHttpRequest. Это неверно.

Ознакомьтесь с расширенными возможностями XMLHttpRequest

Он поддерживается всеми * современными браузерами. И я могу подтвердить, что использую этот подход, поскольку существует XMLHttpRequest 2. У меня никогда не было проблем во всех браузерах, которые я использую.

onreadystatechange полезен, если вы хотите получить заголовки в состоянии 2.

Использование имени XMLHttpRequestпеременной - еще одна большая ошибка, поскольку вам нужно выполнить обратный вызов внутри закрытий onload / oreadystatechange, иначе вы его потеряли.


Теперь, если вам нужно что-то более сложное, используя post и Formfunction x (a, b, e, d, c) {// URL, обратный вызов, метод, formdata или {key: val}, placeholder c = new XMLHttpRequest; c.open (e || 'get', a); c.onload = b; c.onerror = ошибка; c.send (d || null)} function error (e) {console.log ('- Error--', this.type); console.log ('this:', this); console.log ('Event:', e)} function displayAjax (e) {console.log (e, this); } x ('WRONGURL', displayAjax); вы можете легко расширить эту функцию:

displayAjax()

Опять же ... это очень короткая функция, но она получает и публикует.

Примеры использования:

this.statusText

Или передать полный элемент формы ( Method not Allowed):

this.type

Или установите некоторые пользовательские значения:

displayAjax()

Как вы видите, я не реализовал синхронизацию ... это плохо.

Сказав это ... почему бы не сделать это легко?


Как упоминалось в комментарии, использование ошибки && synchronous полностью нарушает точку ответа. Какой хороший способ использовать Ajax правильно?

Обработчик ошибок

function omg(a, c){ // URL
  c = new XMLHttpRequest;
  c.open('GET', a, true);
  c.send();
  return c; // Or c.response
}

В приведенном выше сценарии у вас есть обработчик ошибок, который статически определяется, поэтому он не ставит под угрозу функцию. Обработчик ошибок также может использоваться для других функций.

Но чтобы действительно выпустить ошибку, единственный способ написать неправильный URL-адрес, и в этом случае каждый браузер выдает ошибку.

Обработчики ошибок могут быть полезны, если вы устанавливаете пользовательские заголовки, устанавливаете responseType в буфер памяти blob или что-то еще ....

Даже если вы передадите «POSTAPAPAP» в качестве метода, он не выдает ошибку.

Даже если вы передаете 'fdggdgilfdghfldj' как форму var res = omg ('thisIsGonnaBlockThePage.txt'); это не приведет к ошибке.

В первом случае ошибка находится внутри function foo() { var data; // or $.get(...).then, or request(...).then, or query(...).then fetch("/echo/json").then(function(response){ data = response.json(); }); return data; } var result = foo(); // result is always undefined no matter what. под thenas .then.

Во втором случае это просто работает. Вы должны проверить на стороне сервера, если вы передали правильные данные.

междоменное недопустимое автоматически выдает ошибку.

В ответе об ошибке отсутствуют коды ошибок.

Существует только тот, dataкоторый установлен на ошибку.

Зачем добавлять обработчик ошибок, если у вас нет контроля над ошибками? Большинство ошибок возвращаются внутри этого в функции обратного вызова then.

Итак: нет необходимости проверять ошибки, если вы можете скопировать и вставить URL-адрес должным образом. ;)

PS: В качестве первого теста я написал x ('x', displayAjax) ..., и он полностью получил ответ ... ??? Поэтому я проверил папку, в которой находится HTML, и появился файл с именем «x.xml». Поэтому, даже если вы забудете расширение своего файла, XMLHttpRequest 2 НАЙДЕТ ЭТО . Я LOL'd


Прочитать файл синхронный

Не делай этого.

Если вы хотите заблокировать браузер на некоторое время, загрузите хороший большой файл txt синхронно.

    function getFive(){
        var data;
        setTimeout(function(){ // set a timer for one second in the future
           data = 5; // after a second, do this
        }, 1000);
        return data;
    }
    document.body.innerHTML = getFive(); // `undefined` here and not 5

Теперь вы можете сделать

data

Другого способа сделать это не асинхронно. (Да, с циклом setTimeout ... но серьезно?)

Еще один момент: если вы работаете с API-интерфейсами или просто используете собственные файлы списка или все, что всегда используете разные функции для каждого запроса ...

Только если у вас есть страница, где вы всегда загружаете один и тот же XML / JSON или что-то еще, вам нужна только одна функция. В этом случае немного измените функцию Ajax и замените b специальной функцией.


Вышеупомянутые функции предназначены для основного использования.

Если вы хотите EXTEND функцию ...

Да, ты можешь.

Я использую много API, и одна из первых функций, которые я интегрирую на каждую страницу HTML, - это первая функция Ajax в этом ответе, только с GET ...

Но вы можете сделать много вещей с XMLHttpRequest 2:

Я создал диспетчер загрузки (используя диапазоны с обеих сторон с помощью возобновления, filereader, файловой системы), различные конвертеры изображений, использующие холст, заполняющие базы данных websql с base64images и многое другое ... Но в этих случаях вы должны создать функцию только для этой цели ... иногда вам нужны blob, буферы массивов, вы можете устанавливать заголовки, переопределять mimetype и есть намного больше ...

Но вопрос здесь в том, как вернуть ответ Ajax ... (я добавил простой способ.)


251

Если вы используете обещания, этот ответ для вас.

Это означает, что AngularJS, jQuery (с отсрочкой), замена родного XHR (undefined), EmberJS, BackboneJS или любая библиотека узлов, которая возвращает обещания.

Ваш код должен быть чем-то вроде этого:

data = 5

Феликс Клинг отлично справился с написанием ответа для людей, использующих jQuery с обратными вызовами для AJAX. У меня есть ответ для родного XHR. Этот ответ предназначен для общего использования обещаний либо на интерфейсе, либо на бэкэнде.


Основная проблема

Модель параллелизма JavaScript в браузере и на сервере с NodeJS / io.js является асинхронной и реактивной .

Всякий раз, когда вы вызываете метод, который возвращает обещание, thenобработчики всегда выполняются асинхронно, то есть после кода ниже, который не находится в thenобработчике.

Это означает , что, когда вы возвращение dataв fetchобработчик вы определили еще не выполнить. Это, в свою очередь, означает, что возвращаемое вами значение не было установлено на правильное значение во времени.

Вот простая аналогия в этом вопросе:

$.get

Значение dataесть, .thenтак как function delay(ms){ // takes amount of milliseconds // returns a new promise return new Promise(function(resolve, reject){ setTimeout(function(){ // when the time is up resolve(); // change the promise to the fulfilled state }, ms); }); } часть еще не выполнена. Скорее всего, это произойдет через секунду, но к тому времени это не имеет отношения к возвращенному значению.

Поскольку операция еще не состоялась (AJAX, серверный вызов, IO, таймер), вы возвращаете значение до того, как запрос получил возможность сообщить вашему коду, что это за значение.

Одним из возможных решений этой проблемы является повторное использование кода , информирование вашей программы о том, что делать, когда расчет завершен. Обещания активно позволяют это, будучи временными (чувствительными к времени) по своей природе.

Быстрое резюме обещаний

Обещание - это ценность с течением времени . Обещания имеют состояние, они начинаются как ожидающие, не имеющие ценности, и могут рассчитывать на:

  • выполнил это означает , что вычисление успешно завершено.
  • отклоненное значение означает, что вычисление потерпело неудачу.

Обещание может изменить только состояния один раз , после чего он всегда будет оставаться в том же состоянии навсегда. Вы можете приложить thenобработчики к обещаниям извлечь их значение и обработать ошибки. function delay(ms){ // takes amount of milliseconds // returns a new promise return new Promise(function(resolve, reject){ setTimeout(function(){ // when the time is up resolve(); // change the promise to the fulfilled state }, ms); }); } function getFive(){ // we're RETURNING the promise, remember, a promise is a wrapper over our value return delay(100).then(function(){ // when the promise is ready return 5; // return the value 5, promises are all about return values }) } // we _have_ to wrap it like this in the call site, we can't access the plain value getFive().then(function(five){ document.body.innerHTML = five; });обработчики позволяют связывать вызовы. Обещания создаются с помощью API-интерфейсов, которые возвращают их . Например, более современная замена AJAX fetchили thenобещание возврата jQuery .

Когда мы вызываем thenобещание и что-то возвращаем, мы получаем обещание обработанной ценности . Если мы вернем другое обещание, мы получим удивительные вещи, но давайте держим наших лошадей.

С обещаниями

Давайте посмотрим, как мы можем решить вышеупомянутую проблему с обещаниями. Во-первых, давайте продемонстрируем наше понимание состояний обещаний сверху, используя конструктор Promise для создания функции задержки:

function foo() {
    // RETURN the promise
    return fetch("/echo/json").then(function(response){
        return response.json(); // process it inside the `then`
    });
}

foo().then(function(response){
    // access the value inside the `then`
})

Теперь, после того, как мы конвертировали setTimeout для использования обещаний, мы можем использовать function* foo(){ // notice the star, this is ES6 so new browsers/node/io only yield 1; yield 2; while(true) yield 3; } его для подсчета:

1,2,3,3,3,3,....

В принципе, вместо того , чтобы возвращать значение , которое мы не можем сделать из модели параллелизма - мы возвращаемся в обертку для значения , которое мы можем UnWrap с then. Это похоже на коробку, которую вы можете открыть then.

Применение этого

Это то же самое для вашего первоначального вызова API, вы можете:

Promise.coroutine

Так что это работает так же хорошо. Мы узнали, что мы не можем возвращать значения из уже асинхронных вызовов, но мы можем использовать обещания и связывать их для выполнения обработки. Теперь мы знаем, как вернуть ответ от асинхронного вызова.

ES2015 (ES6)

ES6 представляет генераторы, которые являются функциями, которые могут возвращаться посередине, а затем возобновлять точку, в которой они были. Это обычно полезно для последовательностей, например:

co

Является функцией, которая возвращает итератор по последовательности, Q.asyncкоторая может быть итерирована. Хотя это интересно само по себе и открывает много возможностей, есть один интересный случай.

Если последовательность, которую мы создаем, представляет собой последовательность действий, а не числа, мы можем приостановить эту функцию всякий раз, когда действие дается и дожидается до того, как мы возобновим работу. Поэтому вместо последовательности чисел нам нужна последовательность будущих значений - то есть: обещания.

Этот несколько сложный, но очень мощный трюк позволяет синхронно писать асинхронный код. Есть несколько «бегунов», которые делают это за вас, написав один из них - несколько коротких строк кода, но выходит за рамки этого ответа. Я буду использовать Bluebird var foo = coroutine(function*(){ var data = yield fetch("/echo/json"); // notice the yield // code here only executes _after_ the request is done return data.json(); // data is defined }); здесь, но есть и другие обертки вроде coили var main = coroutine(function*(){ var bar = yield foo(); // wait our earlier coroutine, it returns a promise // server call done here, code below executes when done var baz = yield fetch("/api/users/"+bar.userid); // depends on foo's result console.log(baz); // runs after both requests done }); main(); .

await

Этот метод возвращает само обещание, которое мы можем использовать из других сопрограмм. Например:

async

ES2016 (ES7)

В ES7 это стандартизировано, сейчас есть несколько предложений, но во всех них вы можете awaitобещать. Это просто «сахара» (лучший синтаксис) для предложения ES6 выше, добавив ключевые слова asyncи awaitключевые слова. Приведем пример:

async function foo(){
    var data = await fetch("/echo/json"); // notice the await
    // code here only executes _after_ the request is done
    return data.json(); // data is defined
}

Он все равно возвращает обещание точно так же :)


197

Вы используете Ajax неправильно. Идея состоит в том, чтобы не возвращать что-либо, а вместо этого передавать данные на вызов, называемый функцией обратного вызова, которая обрабатывает данные.

То есть:

function handleData( responseData ) {

    // Do what you want with the data
    console.log(responseData);
}

$.ajax({
    url: "hi.php",
    ...
    success: function ( data, status, XHR ) {
        handleData(data);
    }
});

Возвращение чего-либо в обработчике отправки ничего не сделает. Вы должны либо передать данные, либо делать то, что хотите, непосредственно внутри функции успеха.

JavaScript, Ajax, асинхронные, ECMAScript-6, ECMAScript-2017,