Питання Як я можу повернути відповідь із асинхронного дзвінка?


У мене є функція 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`.

4320
2018-01-08 17:06


походження


@MaximShoustin Я додав тільки відповідь JavaScript (не jQuery), але, як сказав Фелікс: "Як я можу повернути відповідь від виклику AJAX", це дійсно хороший спосіб сказати: "Як працює паралельність в JavaScript". Не кажучи вже про Angular's $http дуже схожий на jQuery $.ajax в будь-якому випадку, з деякими незначними відмінностями (наприклад, взаємодія з циклом дайджестів). - Benjamin Gruenbaum
Не використовувати async: false. Це заморозить браузер і відключить користувачів. Читайте blog.slaks.net/2015-01-04/async-method-patterns щоб навчитися працювати з асинхронними операціями. - SLaks
Можливо, як ви зробили це, вказавши async: false як атрибут першого параметра. Подивіться на мою відповідь нижче. @ tvanfosson дало це рішення на Stackoverflow. - arrowman
це мені допоможе тобі codingbin.com/get-return-data-ajax-call - Manoj Kumar
@SLaks Хтось, будь ласка, передайте це на сайт "розробників рок-зірок" на AirAsia.com. Це чудове дослідження з використанням живого випадку (посилюється за допомогою дзвінків ajax, що приймають пару секунд), чому ніколи не слід використовувати async: false. - Mâtt Frëëman


Відповіді:


-> Більш загальне пояснення асинхронної поведінки з різними прикладами див  Чому моя змінна незмінна після модифікації всередині функції? - Асинхронний кодове посилання 

-> Якщо ви вже розумієте проблему, перейдіть до можливих рішень нижче.

Проблема

The А. в Ajax виступає за асинхронний . Це означає, що надсилання запиту (або, швидше, отримання відповіді) вилучається із звичайного потоку виконання. У вашому прикладі $.ajax повертає негайно і наступне твердження return result;, виконується до виконання функції, яку ви передали success зворотний виклик навіть був викликаний.

Ось аналогія, яка, як ми сподіваємось, робить різницю між синхронним та асинхронним потоком чіткіше:

Синхронний

Уявіть собі, що ви телефонуєте другові і попросіть його шукати щось для вас. Хоча це може зайняти деякий час, ви будете чекати на телефоні і дивитись у просторі, поки ваш друг не дасть вам відповідь, яка вам потрібна.

Те саме відбувається при виклику функції, що містить "нормальний" код:

function findItem() {
    var item;
    while(item_not_found) {
        // search
    }
    return item;
}

var item = findItem();

// Do something with item
doSomethingElse();

Незважаючи на findItem може зайняти багато часу, щоб виконати, будь-який код, що йде після var item = findItem(); має чекати поки функція не поверне результат.

Асинхронний

Ви знову телефонуєте своїм другом з тієї ж причини. Але на цей раз ти йому кажеш, що ти поспішаєш і повинен зателефонуй вам назад на вашому мобільному телефоні. Ви зависаєте, покиньте будинок і робите все, що плануєте зробити. Коли ваш друг зателефонує вам, ви маєте справу з інформацією, яку він дав вам.

Це саме те, що відбувається, коли ви виконуєте запит Ajax.

findItem(function(item) {
    // Do something with item
});
doSomethingElse();

Замість того, щоб чекати відповіді, виконання виконується негайно, і заява після виклику Ajax виконується. Щоб отримати відповідь в кінці кінців, ви надасте функцію, яка буде викликана після отримання відповіді, а зворотний виклик (помітиш щось? зателефонуйте ?) Будь-яка заява після цього дзвінка виконується перед тим, як викликати зворотний виклик.


Рішення (и)

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

Чому погано ти питаєш?

JavaScript запускається в потоці інтерфейсу браузера, і будь-який тривалий процес заблокує інтерфейс користувача, зробить його невідповідним. Крім того, існує верхня межа часу виконання для JavaScript, і браузер запитає користувача про продовження виконання чи ні.

Все це дуже погано. Користувач не зможе визначити, чи все працює нормально чи ні. Крім того, для користувачів з повільним з'єднанням цей ефект буде гіршим.

Нижче ми розглянемо три різні варіанти рішення, які всі будують на вершині один одного:

  • Обіцянки з async/await (ES2017 +, доступний у старих браузерах, якщо ви використовуєте трансплікатор або регенератор)
  • Зворотні виклики (популярний у вузлі)
  • Обіцянки з then() (ES2015 +, доступний у старих веб-переглядачах, якщо ви використовуєте одну з багатьох бібліотек, що мають обіцянки)

Всі три доступні в поточних браузерах та вузлі 7+. 


ES2017 +: обіцянки з async/await

Введена нова версія ECMAScript, випущена в 2017 році Підтримка на рівні синтаксису для асинхронних функцій. За допомогою async і await, ви можете написати асинхронний в "синхронному стилі". Не помиляйтеся, однак: код все одно асинхронний, але його простіше читати / розуміти.

async/await спирається на обіцянки: а async Функція завжди повертає обіцянку. await "розкручує" обіцянку і може призвести до того, що обіцянку було вирішено, або видає помилку, якщо обіцянку було відхилено.

Важливо: Ви можете використовувати тільки await всередині ан async функція Це означає, що на самому вищому рівні вам все одно доведеться працювати безпосередньо з обіцянкою.

Ви можете прочитати більше async і await на MDN

Ось приклад, який спирається на верхню частину затримки вище:

// 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 a second (just for the sake of this example)
    await delay(1000);
    // 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);
  });

Новіше браузер і вузол підтримка версій async/await. Ви також можете підтримувати старі середовища, перетворивши свій код на ES5 за допомогою регенератор (або інструменти, що використовують регенератор, наприклад Вавилон)


Дозволити функції приймати зворотні виклики

Зворотний дзвінок - це просто функція, передана іншій функції. Ця інша функція може викликати виконувану функцію, коли вона готова. У контексті асинхронного процесу, зворотний виклик буде викликатися кожного разу, коли процес асинхронного завершується. Зазвичай результат передається зворотному виклику.

У прикладі питання ви можете зробити foo прийняти зворотний виклик і використовувати його як success зворотний виклик Так це

var result = foo();
// Code that depends on 'result'

стає

foo(function(result) {
    // Code that depends on 'result'
});

Тут ми визначили функцію "inline", але ви можете передати будь-яку функцію посилання:

function myCallback(result) {
    // Code that depends on 'result'
}

foo(myCallback);

foo сама визначається наступним чином:

function foo(callback) {
    $.ajax({
        // ...
        success: callback
    });
}

callback буде посилатися на функцію, до якої ми переходимо foo коли ми називаємо це, і ми просто передаємо це success. І.е. як тільки запит Ajax буде успішним $.ajax зателефоную callback і передайте відповідь на зворотний виклик (з яким можна поговорити з result, оскільки саме так ми визначили зворотний виклик).

Ви також можете обробляти відповідь, перш ніж передавати його на зворотний виклик:

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

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


ES2015 +: обіцянки з потім()

The Promise API це нова функція ECMAScript 6 (ES2015), але це добре Підтримка браузера вже Існує також багато бібліотек, які реалізують стандартний API обіцянок та надають додаткові методи для полегшення використання та складання асинхронних функцій (наприклад, bluebird)

Обіцянки - контейнери для майбутнє цінності Коли обіцянка отримує значення (це є вирішено) або коли його скасовують (відхилено), він повідомляє всіх своїх "слухачів", які хочуть отримати доступ до цього значення.

Перевага перед простими зворотними викликами полягає в тому, що вони дозволяють відокремити ваш код, і їх легше скласти.

Ось простий приклад використання обіцянки:

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).
  });

Застосовуючись до нашого виклику Ajax, ми могли б використовувати такі обіцянки:

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
  });

Характеристика всіх переваг, які обіцяють пропозицію, виходить за рамки цієї відповіді, але якщо ви пишете новий код, слід серйозно розглянути їх. Вони забезпечують чудову абстракцію та поділ вашого коду.

Більше інформації про обіцянки: HTML5 скелі - JavaScript обіцянки

Сторона примітка: відкладені об'єкти jQuery

Відкладені об'єкти Користувальницьке виконання обіцянок jQuery (до того, як API Promise було стандартизовано). Вони поводяться майже як обіцянки, але виявляють дещо інший API.

Кожний Ajax-метод jQuery вже повертає "відкладене об'єкт" (фактично, обіцянка відстроченого об'єкта), який ви можете просто повернутися з вашої функції:

function ajax() {
    return $.ajax(...);
}

ajax().done(function(result) {
    // Code depending on result
}).fail(function() {
    // An error occurred
});

Сторона примітка: Обіцянка gotchas

Пам'ятайте, що обіцянки та відкладені об'єкти є справедливими контейнери для майбутньої цінності вони не є самою цінністю. Наприклад, припустімо, що ви мали наступне:

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() не заморожує код, перевіряючи сторінку "/ пароль" на своєму сервері - він надсилає запит на сервер і, поки він чекає, негайно повертає об'єкт відстрочення jQuery Ajax, а не відповідь з сервера. Це означає, що if твердження збирається завжди отримати цей відкладений об'єкт, розглядати його як true, і продовжуйте, як ніби користувач увійшов до системи. Не добре.

Але виправити це просто:

checkPassword()
.done(function(r) {
    if (r) {
        // Tell the user they're logged in
    } else {
        // Tell the user their password was bad
    }
})
.fail(function(x) {
    // Tell the user something bad happened
});

Не рекомендується: Синхронний виклик "Ajax"

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

Без jQuery

Якщо ви безпосередньо використовуєте a XMLHTTPRequest об'єкт, пройти false як третій аргумент до .open.

jQuery

Якщо ви використовуєте jQuery, ви можете встановити async варіант до false. Зауважте, що цей параметр є не підтримується оскільки jQuery 1.8. Потім ви можете або використовувати a success зворотний виклик або доступ до responseText власність об'єкт jqXHR:

function foo() {
    var jqXHR = $.ajax({
        //...
        async: false
    });
    return jqXHR.responseText;
}

Якщо ви використовуєте будь-який інший метод jQuery Ajax, наприклад $.get, $.getJSONі т. д., ви повинні змінити його на $.ajax(оскільки ви можете передавати параметри конфігурації тільки для $.ajax)

Голови вгору! Це неможливо зробити синхронно JSONP запит JSONP за своєю природою завжди асинхронний (ще одна причина не враховувати цей варіант).


4666
2018-01-08 17:06



@Pommy: Якщо ви хочете використовувати jQuery, ви повинні включити його. Будь ласка зверніться до docs.jquery.com/Tutorials:Getting_Started_with_jQuery. - Felix Kling
У рішенні 1, під jQuery, я не міг зрозуміти цю лінію: If you use any other jQuery AJAX method, such as $.get, $.getJSON, etc., you have them to $.ajax. (Так, я усвідомлюю, що мій нік є таємним іронічним у цьому випадку) - gibberish
@gibberish: Мммм, я не знаю, як це можна зробити чіткіше. Ви бачите, як це зробити foo називається і до нього передається функція (foo(function(result) {....});)? result використовується усередині цієї функції і є відповіддю на запит Ajax. Щоб послатися на цю функцію, називається перший параметр foo callback і призначений success замість анонімної функції. Так, $.ajax зателефоную callback коли запит був успішним. Я намагався пояснити це трохи більше. - Felix Kling
Чат для цього питання є мертвим, тому я не знаю, де пропонувати окремі зміни, але я пропоную: 1) змінити синхронну частину на просте обговорення того, чому це погано, без прикладу коду, як це зробити. 2) Видаліть / об'єднайте приклади зворотного виклику, щоб показати більш гнучкий метод відкладеного підходу, який, на мою думку, може бути трохи простішим для тих, хто вивчає Javascript. - Chris Moschini
@Jessi: Я думаю, ти неправильно зрозумів цю частину відповіді. Ви не можете використовувати $.getJSON якщо ви хочете, щоб запит Ajax був синхронним. Тим не менше, ви не маєте бажання, щоб запит був синхронним, тому він не застосовувався. Ви повинні використовувати зворотні виклики або обіцянки обробляти відповідь, як це пояснено раніше у відповідь. - Felix Kling


Якщо ти ні використовуючи jQuery у вашому коді, ця відповідь для вас

Ваш код має бути чимось на кшталт цього:

function foo() {
    var httpRequest = new XMLHttpRequest();
    httpRequest.open('GET', "/echo/json");
    httpRequest.send();
    return httpRequest.responseText;
}

var result = foo(); // always ends up being 'undefined'

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

(Зауважте, для тих, хто використовує новий fetch API, кутовий або обіцянки Я додав ще одну відповідь нижче)


Що ви стикаєтеся

Це короткий виклад "Пояснення проблеми" з іншої відповіді, якщо ви не впевнені, прочитавши це, прочитайте це.

The А. в AJAX означає асинхронний. Це означає, що надсилання запиту (або, швидше, отримання відповіді) вилучається із звичайного потоку виконання. У вашому прикладі .send повертає негайно і наступне твердження return result;, виконується до виконання функції, яку ви передали success зворотний виклик навіть був викликаний.

Це означає, що коли ви повертаєтесь, визначений вами слухач ще не виконується, а це означає, що значення, яке ви повертаєте, не було визначено.

Ось проста аналогія

function getFive(){ 
    var a;
    setTimeout(function(){
         a=5;
    },10);
    return a;
}

(Скрипка)

Значення a повертається є undefined з a=5 частина ще не виконана. AJAX діє подібно до цього, ви повертаєте значення, перш ніж сервер отримає можливість повідомити браузеру, що таке значення.

Одним з можливих шляхів вирішення цієї проблеми є кодування повторно активно , кажучи вашої програмі, що робити, коли розрахунок завершено.

function onComplete(a){ // When the code completes, do this
    alert(a);
}

function getFive(whenDone){ 
    var a;
    setTimeout(function(){
         a=5;
         whenDone(a);
    },10);
}

Це називається CPS. В принципі ми проходимо getFive дія, яку потрібно виконати, коли вона закінчиться, ми кажемо нашому кодексу, як реагувати, коли подія закінчиться (наприклад, наш AJAX дзвінок або в цьому випадку тайм-аут).

Використання буде:

getFive(onComplete);

Який повинен сповістити "5" на екран. (Скрипка).

Можливі рішення

Існує два способи вирішення цього:

  1. Зробити виклик AJAX синхронним (дозвольте назвати його SJAX).
  2. Реструктуруйте свій код, щоб правильно працювати з зворотними викликами.

1. Синхронний AJAX - не роби це!

Що стосується синхронного AJAX, не роби це! Відповідь Фелікса висуває кілька переконливих аргументів про те, чому це погана ідея. Підбиваючи підсумок, він заморозить браузер користувача, доки сервер не поверне відповідь і не створюватиме дуже погану взаємодію з користувачем. Ось ще одне коротке резюме з MDN про те, чому:

XMLHttpRequest підтримує як синхронні, так і асинхронні комунікації. Взагалі, однак, асинхронні запити повинні бути кращими, ніж синхронні запити з міркувань продуктивності.

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

Якщо ви мати Щоб зробити це, ви можете передати прапор: Ось як:

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);
}

2. Переробити код

Нехай ваша функція приймає зворотний виклик. У прикладі коду foo можна прийняти зворотний виклик. Ми будемо говорити про наш код, як це зробити реагувати коли foo завершує

Так:

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

Стає:

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

Тут ми передали анонімну функцію, але ми могли так само легко перенести посилання на існуючу функцію, щоб вона виглядала так:

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

Щоб отримати докладніші відомості про те, як цей тип зворотного виклику завершено, перевірте відповідь Фелікса.

Тепер давайте визначимо себе, щоб діяти відповідно

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();
}

(скрипка)

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

Якщо вам все ще важко зрозуміти це прочитайте посібник із початку роботи з AJAX на MDN


888
2018-05-29 23:30



"синхронні запити блокують виконання коду і можуть витоку пам'яті та подій", як може синхронний запит витоку пам'яті? - Matthew G
@MatthewG Я додав в неї щедрість це питання, Я побачу, що я можу рибати. Я тимчасово вилучаю цитату з відповіді. - Benjamin Gruenbaum
Тільки для посилання, XHR 2 дозволяє нам використовувати onload обробник, який тільки запускається, коли readyState є 4. Звичайно, це не підтримується в IE8. (iirc, може знадобитися підтвердження). - Florian Margaine
Ваше пояснення того, як передавати анонімну функцію як зворотний виклик, є дійсним, але неправдивим. Приклад var bar = foo (); просить визначити змінну, тоді як ваш запропонований foo (functim () {}); не визначає панель - Robbie Averill
@BenjaminGruenbaum Як повернути вміст result в змінній, яку я можу використати де-небудь ще в моєму коді, а не друкувати його з html? - MorganFR


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

Якщо ви не використовуєте jQuery і хочете приємного короткого XMLHttpRequest 2, який працює в сучасних браузерах, а також у мобільних браузерах, я пропоную використовувати його таким чином:

function ajax(a, b, c){ // URL, callback, just a placeholder
  c = new XMLHttpRequest;
  c.open('GET', a);
  c.onload = b;
  c.send()
}

Як ви можете бачити:

  1. Це коротше, ніж у всіх інших функціях.
  2. Зворотний дзвінок встановлюється безпосередньо (так що немає додаткових зайвих замків).
  3. Він використовує нову онлайнову завантаження (тому вам не потрібно перевіряти readystate && статус)
  4. Є й інші ситуації, які я не пам'ятаю, що змушують XMLHttpRequest 1 дратувати.

Є два способи отримати відповідь на цей Ajax-виклик (три з використанням імені ресурсу XMLHttpRequest):

Найпростіше:

this.response

Або якщо з якоїсь причини ви bind() зворотний виклик до класу:

e.target.response

Приклад:

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

Або (вище, краще анонімні функції завжди є проблемою):

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

Нічого не простіше.

Зараз деякі люди, ймовірно, скажуть, що краще використовувати назву змінної XMLHttpRequest на рівні змінної або навіть XMLHttpRequest. Це неправильно.

Перевірити Додаткові функції XMLHttpRequest

Він підтримується на всіх * сучасних браузерах. І я можу підтвердити, оскільки я використовую цей підхід, оскільки XMLHttpRequest 2 існує. У мене ніколи не було проблем з усіма браузерами, якими я користуюсь.

onreadystatechange корисний лише якщо ви хочете отримати заголовки на стані 2.

Використовуючи XMLHttpRequest Ім'я змінної є ще однією великою помилкою, оскільки вам потрібно виконати зворотній виклик всередині закладок onload / oreadystatechange, інакше ви втратили його.


Тепер, якщо ви хочете щось більш складне, використовуючи пошту та FormData, ви можете легко розширити цю функцію:

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);

Як видно, я не реалізував синхронізацію ... це погано.

Сказавши це ... чому це не простий спосіб?


Як згадується в коментарях, використання помилки && синхронного повністю зламає точку відповіді. Який хороший короткий спосіб використовувати Ajax належним чином?

Обробник помилок

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.onerror = error;
  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);

У наведеному вище сценарії у вас є обробник помилок, який статично визначено, щоб це не поставило під загрозу функцію. Обробник помилок може бути використаний і для інших функцій.

Але дійсно вийти з помилки в тільки Шлях - це написати неправильну URL-адресу, в цьому випадку кожен браузер видає помилку.

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

Навіть якщо ви передаєте "POSTAPAPAP" як метод, він не буде викидати помилку.

Навіть якщо ви передаєте 'fdggdgilfdghfldj' як формат, він не буде викидати помилку.

У першому випадку помилка знаходиться всередині displayAjax() під this.statusText як Method not Allowed.

У другому випадку це просто працює. Ви повинні перевірити на стороні сервера, якщо ви передали правильні дані пошти.

Перехресні домены не допускаються автоматично.

У відповідь на помилку немає кодів помилок.

Є тільки this.type яка встановлена ​​на помилку.

Чому додати обробника помилок, якщо ви абсолютно не контролюєте помилки? Більшість помилок повертаються всередині цього в функції зворотного виклику displayAjax().

Отже: немає необхідності перевіряти помилки, якщо ви можете правильно скопіювати та вставити URL-адресу. ;)

PS: Як перший тест я написав x ('x', displayAjax) ..., і це повністю отримало відповідь ... ??? Таким чином, я перевірив папку, в якій знаходиться HTML, і був файл з назвою "x.xml". Таким чином, навіть якщо ви забудете розширення вашого файлу, XMLHttpRequest 2 знайде його. Я LOL'd


Читати файл синхронно

Не робіть цього.

Якщо ви хочете тимчасово заблокувати браузер, завантажте хороший великий файл TXT синхронно.

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

Тепер ти можеш це зробити

 var res = omg('thisIsGonnaBlockThePage.txt');

Існує не інший спосіб зробити це не асинхронним способом. (Так, з циклом setTimeout ... але серйозно?)

Інший момент - якщо ви працюєте з інтерфейсом API, або просто у вас є файли списку, тощо, для кожного запиту завжди використовуються різні функції ...

Тільки якщо у вас є сторінка, на якій ви завжди завантажуєте той самий XML / JSON, або що вам потрібно, лише одну функцію. У такому випадку трохи змініть функцію Ajax і замініть b своєю спеціальною функцією.


Функції, описані вище, призначені для базового використання.

Якщо ви хочете EXTEND функцію ...

Так, ти можеш.

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

Але ви можете зробити багато чого з XMLHttpRequest 2:

Я створив менеджера завантаження (використовуючи діапазони з обох сторін з резюме, filereader, файлової системи), різні перетворювачі зображень зображень, що використовують полотно, заповнювати бази даних websql з базою64образ і багато іншого ... Але в таких випадках вам слід створити функцію лише для цієї мети ... іноді вам потрібні буфери, масивні буфери, ви можете встановити заголовки, замінити mіmtype і є набагато більше ...

Але питання тут, як повернути відповідь Ajax ... (Я додав простий спосіб.)


304
2017-08-19 08:06



Хоча ця відповідь гарна (І ми всі кохання XHR2 та публікація файлів даних та багатопартійних даних є абсолютно чудовим) - це показує синтаксичний цукор для розміщення XHR з JavaScript - можливо, ви захочете розмістити це в публікації в блозі (я хотів би це) або навіть у бібліотеці (не знаю про ім'я x, ajax або xhr може бути краще :)) Я не бачу, як це рішення повертає відповідь з виклику AJAX. (хтось ще може зробити var res = x("url") і не розумію, чому це не працює;)). На бічній ноті - було б здорово, якщо б ви повернулися c від методу, таким чином користувачі можуть зачепитися error тощо. - Benjamin Gruenbaum
2.ajax is meant to be async.. so NO var res=x('url').. Це цілковита точка цього питання і відповіді :) - Benjamin Gruenbaum
чому у функціях є параметр "c", якщо на першому рядку ви перезаписуєте будь-яке значення, яке він мав? я щось втрачаю? - Brian H.
Ви можете використовувати параметри як заповнювач, щоб не писати кілька разів "var" - cocco
@cocco Таким чином, ви написали вводять в оману неправдивий, нечитабельний код відповісти щоб зберегти кілька клавіш? Будь ласка, не робіть цього. - stone


Якщо ви використовуєте обіцянки, ця відповідь для вас.

Це означає, що AngularJS, jQuery (з відкладеними), місцева XHR заміна (fetch), EmberJS, BackboneJS's або будь-яка бібліотека вузлів, яка повертає обіцянки.

Ваш код має бути чимось на кшталт цього:

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.

Фелікс Клінг виконав чудову роботу, написавши відповідь для людей, які використовують jQuery з зворотними викликами для AJAX. У мене є відповідь для рідного XHR. Ця відповідь полягає у загальному використанні обіцянок або на інтерфейсі, або на бекенде.


Основне питання

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

Кожного разу, коли ви викликаєте метод, який повертає обіцянку, then обробники є завжди виконується асинхронно - тобто після код нижче їх, що не в а .then обробник

Це означає, коли ви повертаєтесь data в then Обробник, який ви визначили, ще не виконано. Це, в свою чергу, означає, що значення, яке ви повертаєте, не було встановлено правильним значенням у часі.

Ось проста аналогія для випуску:

    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 є undefined з data = 5 частина ще не виконана. Воно, ймовірно, буде виконуватися через секунду, але до цього часу це не має значення для поверненого значення.

Оскільки операцію ще не відбулося (AJAX, виклик сервера, IO, таймер), ви повертаєте значення, перш ніж запит отримав шанс сказати вашому коду, що це значення.

Одним з можливих шляхів вирішення цієї проблеми є кодування повторно активно , кажучи вашої програмі, що робити, коли розрахунок завершено. Обіцянки активно дозволяють це, будучи тимчасовими (враховуючи часові) характеру.

Швидкий відгук про обіцянки

Обіцянка - це вартість у часі. Обіцянки мають державу, вони починаються як очікувані без будь-якої цінності і можуть вирішити:

  • виконано це означає, що обчислення успішно завершено.
  • відхилено це означає, що обчислення не відбулося.

Обіцянка може змінювати лише держави один раз після чого він завжди залишатиметься в одній державі назавжди. Ви можете приєднати then Оброблювачі обіцяють витягувати свою цінність і виконувати помилки. then обробники дозволяють ланцюгування дзвінків. Обіцянки створюються використовуючи API, які повертають їх. Наприклад, більш сучасна заміна AJAX fetch або jQuery $.get повернення обіцянок.

Коли ми кличемо .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);
    });
}

Тепер, після того, як ми перетворили setTimeout на використання обіцянок, ми можемо використовувати 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;
});

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

Застосувати це

Це відповідає вашому вихідному виклику API, ви можете:

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`
})

Так що це так само добре. Ми дізналися, що ми не можемо повернути значення з вже асинхронних дзвінків, але ми можемо використовувати обіцянки та ланцюгувати їх для виконання обробки. Тепер ми знаємо, як повернути відповідь з асинхронного дзвінка.

ES2015 (ES6)

ES6 вводить генератори які є функціями, які можуть повернутись посередині, а потім відновити точку, в якій вони перебували. Це, як правило, корисно для послідовностей, наприклад:

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,.... який може бути повторений. Хоча це цікаво самостійно і відкриває простір для великої можливості, є один особливий цікавий випадок.

Якщо послідовність, яку ми виробляємо, - це послідовність дій, а не цифр, - ми можемо призупинити функцію кожного разу, коли дія буде отримана, і чекати її перед тим, як ми відновимо функцію. Тому замість послідовності чисел нам потрібна послідовність числа майбутнє цінності - це: обіцянки.

Цей дещо складний, але дуже потужний трюк дозволяє нам синхронно створювати асинхронний код. Існує декілька "бігунів", які роблять це для вас, написавши це короткий, кілька рядків коду, але виходить за рамки цієї відповіді. Я буду використовувати Bluebird's Promise.coroutine тут, але є й інші обгортки, як co або Q.async.

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
});

Цей метод повертає саму обіцянку, яку ми можемо споживати з інших кортиней. Наприклад:

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();

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
}

Це все одно повертає обіцянку так само :)


245
2018-05-12 02:22



ES 2016 виглядає як остаточне рішення цієї проблеми раз і назавжди. - ShrekOverflow
Абсолютно остаточне рішення, раз і назавжди? Як щодо абсолютно остаточного рішення, раз і назавжди плюс один? - Shmiddty
де пекло ви отримуєте "п'ять" з? там ніколи не оголошується. - recurf
@recurf перш за все "де пекло" навряд чи підходить для цього сайту (діти використовують його теж для одного). Другий - це параметр функції, я міг би назвати це деінде, і він буде працювати так само добре. - Benjamin Gruenbaum
Ви відповіли на питання? Як я можу повернути відповідь із асинхронного дзвінка? @ BenjaminGruenbaum - OnPrinciples


Ви використовуєте 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);
    }
});

Повернення що-небудь у обробник відправки нічого не зробить. Замість цього ви повинні передавати дані або робити те, що ви хочете, безпосередньо в рамках функції успіху.


193
2018-05-23 02:05



Ця відповідь повністю семантична ... ваш метод успіху - це зворотній виклик у зворотному виділенні. Ви могли б просто мати success: handleData і це буде працювати. - Jacques
А що, якщо ви хочете повернути "responseData" поза "handleData" ... :) ... як ви це зробите ...? ... тому що простий повернення поверне його до зворотного дзвінка "успіху" ajax ..., а не за "handleData" ... - pesho hristov
@Jacques & @pesho hristov Ви пропустили цей пункт. Відправити обробник не є success Метод, це навколишня сфера діяльності $.ajax. - travnik
@travnik я цього не пропустив. Якщо ви взяли вміст handleData і поставили його в метод успіху, він буде діяти точно так само ... - Jacques


Найпростіше рішення - створити функцію JavaScript і назвати його для Ajax success зворотний виклик

function callServerAsync(){
    $.ajax({
        url: '...',
        success: function(response) {

            successCallback(response);
        }
    });
}

function successCallback(responseObj){
    // Do something like read the response and show data
    alert(JSON.stringify(responseObj)); // Only applicable to JSON response
}

function foo(callback) {

    $.ajax({
        url: '...',
        success: function(response) {
           return callback(null, response);
        }
    });
}

var result = foo(function(err, result){
          if (!err)
           console.log(result);    
}); 

185
2018-02-18 18:58



Я не знаю, хто голосував за нього негативно. Але це робота, навколо якої працює насправді, я використовував цей підхід для створення цілієї програми. Jquery.ajax не повертає дані, щоб краще використовувати наведені вище підходи. Якщо це неправильно, будь ласка, поясніть та запропонуйте кращий спосіб зробити це. - Hemant Bavle
Вибачте, я забув залишити коментар (я зазвичай роблю!). Я зрівняв це. Вниз не вказує на фактичну правильність чи відсутність, вони вказують на корисність у контексті або відсутність. Я не вважаю вашу відповідь корисною, надану Феліксом, яка вже пояснює це лише більш детально. На бічній ноті, чому б ви стиснути відповідь, якщо це JSON? - Benjamin Gruenbaum
Добре .. @Benjamin я використав stringify, щоб перетворити об'єкт JSON в рядок. І дякую за роз'яснення своєї думки. Майте на увазі, щоб написати більш складні відповіді. - Hemant Bavle
А що, якщо ви хочете повернути "responseObj" поза "successcallback" ... :) ... як ви це зробите ...? ... через те, що простий повернення поверне його до зворотного дзвінка "успіху" ajax ..., а не поза "successCallback" ... - pesho hristov


Я відповім з жахливою комедією, намальованою рукою. Друге зображення - це причина result є undefined у вашому прикладі коду.

enter image description here


155
2017-08-11 14:17



Картина варта тисячі слів, Особа А - Запитайте людині B деталі, щоб виправити свою машину, у свою чергу Особа Б. - Робить Ajax Call і чекає відповіді від сервера на деталі для фіксації автомобіля, після отримання відповіді, функція Ajax Success викликає функцію Person B і передає відповідь як аргумент, Person A отримує відповідь. - stom
Будемо чудово, якщо ви додасте рядки коду з кожним зображенням, щоб ілюструвати поняття. - Hassan Baig
Найкраще пояснення, яке я коли-небудь зустрічав - anonymous