비동기 호출에서 응답을 반환하는 방법

javascript jquery ajax asynchronous


Ajax 요청 을하는 함수 foo 가 있습니다. 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`.



Answer 1 Felix Kling


→ 다른 예제와의 비동기 동작에 대한 일반적인 설명은 함수 내에서 변수를 수정 한 후 왜 변수가 변경되지 않습니까? 를 참조하십시오 . -비동기 코드 참조

→ 문제를 이미 이해 한 경우 아래 가능한 해결 방법으로 건너 뛰십시오.

문제

AjaxA비동기를 나타냅니다 . 즉, 요청 전송 (또는 응답 수신)은 정상적인 실행 흐름에서 제외됩니다. 귀하의 예에서 $.ajax 는 즉시 리턴하고 다음 명령문은 return result; success 콜백이 호출 된 후에 전달한 함수 전에 실행 됩니다.

다음은 동기 흐름과 비동기 흐름의 차이를 더 명확하게 만드는 유추입니다.

Synchronous

친구에게 전화를 걸어 무언가를 찾아 보라고 상상해보십시오. 시간이 걸릴 수 있지만 친구가 필요한 답변을 줄 때까지 전화를 기다리고 우주를 응시합니다.

"정상"코드를 포함하는 함수를 호출 할 때도 마찬가지입니다.

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

var item = findItem();

// Do something with item
doSomethingElse();

findItem 을 실행하는 데 시간이 오래 걸릴 수 있지만 모든 코드는 var item = findItem(); 함수가 결과를 반환 할 까지 기다려야 합니다.

Asynchronous

같은 이유로 친구에게 다시 전화하십시오. 그러나 이번에는 당신이 그에게 당신이 서두르고 있다고 말하면 휴대 전화로 다시 전화 해야합니다 . 당신은 전화를 끊고 집을 떠나고 당신이 계획 한 일을합니다. 친구가 당신에게 전화하면, 당신은 그가 당신에게 준 정보를 다루고 있습니다.

그것이 바로 Ajax 요청을 할 때 일어나는 일입니다.

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

응답을 기다리는 대신 실행이 즉시 계속되고 Ajax 호출 후 명령문이 실행됩니다. 결국 응답을 얻으려면, 당신은 응답이 수신되면 호출되는 함수, 제공하는 콜백 (통지 뭔가? 전화 다시 ?). 콜백이 호출되기 전에 해당 호출 다음에 오는 명령문이 실행됩니다.


Solution(s)

JavaScript의 비동기 특성을 수용하십시오! 특정 비동기 작업은 동기 상대를 제공하지만 ( "Ajax"도 마찬가지) 일반적으로 브라우저 컨텍스트에서 사용하는 것은 바람직하지 않습니다.

왜 나쁜가요?

JavaScript는 브라우저의 UI 스레드에서 실행되며 장시간 실행되는 프로세스는 UI를 잠그므로 응답하지 않습니다. 또한 JavaScript의 실행 시간에는 상한이 있으며 브라우저는 사용자에게 계속 실행 여부를 묻습니다.

이 모든 것은 정말 나쁜 사용자 경험입니다. 사용자는 모든 것이 제대로 작동하는지 알 수 없습니다. 또한 연결 속도가 느린 사용자에게는 효과가 더 나빠질 수 있습니다.

다음에서 우리는 서로 위에 세 가지 다른 솔루션을 살펴볼 것입니다.

  • async/await 약속합니다 (ES2017 +, 트랜스 파일러 또는 재생기를 사용하는 경우 이전 브라우저에서 사용 가능)
  • 콜백 (인기 노드)
  • then() 약속 (ES2015 +, 많은 약속 라이브러리 중 하나를 사용하는 경우 이전 브라우저에서 사용 가능)

세 가지 모두 현재 브라우저 및 노드 7 이상에서 사용할 수 있습니다.


ES2017 + : async/await 약속

2017 년에 출시 된 ECMAScript 버전 에는 비동기 함수에 대한 구문 수준의 지원이 도입되었습니다 . 의 도움으로 asyncawait 를 , 당신은 "동기 스타일"에서 비동기 쓸 수 있습니다. 코드는 여전히 비동기 적이지만 읽기 / 이해하기가 더 쉽습니다.

async/await 는 약속 위에 빌드됩니다. async 함수는 항상 약속을 반환합니다. await 를 "펼쳤다"약속으로 해결되거나 약속이 거부 된 경우 오류가 발생 된 약속과 가치 중 결과.

중요 : async 함수 내 에서만 await 를 사용할 수 있습니다 . 현재 최상위 레벨 await 는 아직 지원되지 않으므로 async 컨텍스트 를 시작하려면 비동기 IIFE ( 즉시 호출 된 함수 표현식 )를 작성해야합니다 .

MDN에서 async await 에 대한 자세한 내용을 읽을 수 있습니다 .

다음은 위의 지연 시간 위에 구축 된 예입니다.

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

// Start an IIFE to use `await` at the top level
(async function(){
  let books = await getAllBooks();
  console.log(books);
})();

현재 브라우저노드 버전은 async/await 를 지원 합니다. 또한 재생기 (또는 Babel 과 같은 재생기를 사용하는 도구)를 사용 하여 코드를 ES5로 변환하여 이전 환경을 지원할 수 있습니다 .


함수가 콜백을 받도록 허용

콜백은 단순히 다른 함수로 전달되는 함수입니다. 다른 함수는 준비가 될 때마다 전달 된 함수를 호출 할 수 있습니다. 비동기 프로세스와 관련하여 콜백은 비동기 프로세스가 완료 될 때마다 호출됩니다. 일반적으로 결과는 콜백으로 전달됩니다.

질문의 예에서 foo 는 콜백을 수락하고 success 콜백 으로 사용할 수 있습니다. 그래서 이건

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

becomes

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

여기에 함수 "인라인"을 정의했지만 모든 함수 참조를 전달할 수 있습니다.

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

foo(myCallback);

foo 자체는 다음과 같이 정의됩니다.

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

callback 은 호출 할 때 foo 에 전달하는 함수를 참조하며 단순히 success 에 전달합니다 . 즉, Ajax 요청이 성공하면 $.ajaxcallback 을 콜하고 콜백 에 응답을 전달합니다 (이것은 콜백 을 정의한 방식이므로 result 로 참조 할 수 있음 ).

콜백에 전달하기 전에 응답을 처리 할 수도 있습니다.

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

콜백을 사용하여 코드를 작성하는 것보다 쉽습니다. 결국, 브라우저의 JavaScript는 많은 이벤트 중심 (DOM 이벤트)입니다. Ajax 응답을받는 것은 이벤트 일뿐입니다.
타사 코드로 작업해야 할 경우 문제가 발생할 수 있지만 대부분의 문제는 응용 프로그램 흐름을 생각하면 해결 될 수 있습니다.


ES2015 + : then ()으로 약속

약속 API는 ECMAScript를 6 (ES2015)의 새로운 기능이지만, 좋은이 브라우저 지원을 이미. 표준 Promises API를 구현하고 비동기 함수 (예 : 블루 버드 ) 의 사용 및 구성을 용이하게하는 추가 메소드를 제공하는 많은 라이브러리도 있습니다 .

약속은 미래 가치를 위한 컨테이너입니다 . 약속이 값을 받거나 ( 해결됨 ) 취소 ( 거부 됨 )하면이 값에 액세스하려는 모든 "리스너"에게 알립니다.

일반 콜백에 비해 장점은 코드를 분리 할 수 ​​있고 작성이 더 쉽다는 것입니다.

다음은 약속을 사용하는 간단한 예입니다.

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 Rocks-JavaScript Promises

참고 사항 : jQuery의 지연된 객체

지연된 객체 는 Promise API가 표준화되기 전에 jQuery의 사용자 지정 약속 구현입니다. 그것들은 거의 약속처럼 행동하지만 약간 다른 API를 노출시킵니다.

jQuery의 모든 Ajax 메소드는 이미 함수에서 리턴 할 수있는 "지연된 오브젝트"(실제로 지연된 오브젝트의 약속)를 리턴합니다.

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

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

참고 사항 : Promise 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() 는 서버에서 '/ password'페이지를 확인하는 동안 코드를 고정하지 않습니다. 서버에 요청을 보내고 대기하는 동안 응답이 아닌 jQuery Ajax Deferred 객체를 즉시 반환합니다. 서버에서. 즉, if 문은 항상이 Deferred 객체를 가져 와서 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없이

XMLHTTPRequest 객체 를 직접 사용하는 경우 .open 세 번째 인수로 false 를 전달하십시오 .

jQuery

jQuery 를 사용하는 경우 async 옵션을 false 로 설정할 수 있습니다 . 이 옵션은 jQuery 1.8부터 더 이상 사용되지 않습니다 . 그런 다음 여전히 success 콜백을 사용하거나 jqXHR 객체responseText 속성에 액세스 할 수 있습니다 .

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

$.get , $.getJSON 등과 같은 다른 jQuery Ajax 메소드를 사용하는 경우 $.ajax 로 변경해야합니다 (구성 매개 변수 만 $.ajax 전달할 수 있으므로 ).

헤즈 업! 동기 JSONP 요청 은 불가능합니다 . JSONP는 본질적으로 항상 비동기 적입니다 (이 옵션을 고려하지 않는 한 가지 이유).




Answer 2 Benjamin Gruenbaum


당신이 경우 되지 코드에서 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'

Felix Kling은 AJAX를 위해 jQuery를 사용하는 사람들을 위해 훌륭한 답변을 작성했으며, 그렇지 않은 사람들에게 대안을 제공하기로 결정했습니다.

( 참고, 새로운 fetch API, Angular 또는 약속을 사용하는 사람들에게는 아래에 다른 답변을 추가했습니다 )


당신이 직면하고있는 것

이것은 다른 답변의 "문제 설명"에 대한 간략한 요약입니다.이를 읽은 후에 확실하지 않으면 읽으십시오.

AJAX 의 A비동기를 나타냅니다 . 즉, 요청 전송 (또는 응답 수신)은 정상적인 실행 흐름에서 제외됩니다. 예제에서 .send 는 즉시 리턴하고 다음 명령문은 return result; success 콜백이 호출 된 후에 전달한 함수 전에 실행 됩니다.

이것은 반환 할 때 정의한 리스너가 아직 실행되지 않았 음을 의미합니다. 이는 반환하는 값이 정의되지 않았 음을 의미합니다.

여기 간단한 비유가 있습니다

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

(Fiddle)

값 리턴이되고 undefined 때문에 a=5 부분이 아직 실행하지 않았다. AJAX는 이와 같이 작동하므로 서버가 브라우저에 해당 값이 무엇인지 알려주기 전에 값을 반환합니다. a

이 문제에 대한 한 가지 가능한 해결책은 재활 성 코드를 작성 하여 계산이 완료되면 프로그램에 지시합니다.

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 가 완료 될 때 반응 하는 방법을 코드에 알려줄 것 입니다.

So:

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

Becomes:

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

여기서 익명 함수를 전달했지만 기존 함수에 대한 참조를 쉽게 전달할 수 있습니다.

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

이러한 종류의 콜백 디자인이 수행되는 방법에 대한 자세한 내용은 Felix의 답변을 확인하십시오.

이제 foo 자체를 적절하게 정의 해 봅시다

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

(fiddle)

이제 우리는 foo 함수가 AJAX가 성공적으로 완료 될 때 실행할 동작을 받아들이도록 만들었습니다. 응답 상태가 200이 아닌지 확인하고 그에 따라 행동하여 (실패 핸들러 등 생성)이를 확장 할 수 있습니다. 효과적으로 우리의 문제를 해결합니다.

여전히 이해하기 어려운 경우 MDN 의 AJAX 시작 안내서 를 읽으십시오 .




Answer 3 cocco


XMLHttpRequest 2 (먼저 Benjamin Gruenbaum & Felix Kling 의 답변을 읽음)

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. 새로운 onload를 사용하므로 readystate && status를 확인할 필요가 없습니다.
  4. 내가 기억하지 못하는 다른 상황이 있는데 XMLHttpRequest 1을 성가 시게 만든다.

이 Ajax 호출에 대한 응답을 얻는 두 가지 방법이 있습니다 (XMLHttpRequest var 이름을 사용하는 세 가지).

가장 간단한 :

this.response

또는 어떤 이유로 든 콜백을 클래스에 bind() 하면

e.target.response

Example:

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

또는 (위의 익명 함수는 항상 문제가됩니다.)

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

쉬운 일이 아닙니다.

이제 일부 사람들은 onreadystatechange 또는 심지어 XMLHttpRequest 변수 이름을 사용하는 것이 더 낫다고 말할 것입니다. 그건 틀렸어요.

XMLHttpRequest 고급 기능 확인

모든 * modern 브라우저를 지원했습니다. XMLHttpRequest 2가 존재하기 때문에이 접근법을 사용하고 있음을 확인할 수 있습니다. 내가 사용하는 모든 브라우저에서 문제가 발생하지 않았습니다.

onreadystatechange는 상태 2에서 헤더를 가져 오려는 경우에만 유용합니다.

XMLHttpRequest 변수 이름을 사용하는 것은 onload / oreadystatechange 클로저 내에서 콜백을 실행해야하므로 손실되는 또 다른 큰 오류입니다.


이제 post 및 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에 오류가 발생하는 잘못된 URL을 작성하는 것입니다.

오류 처리기는 사용자 지정 헤더를 설정하고 responseType을 blob 배열 버퍼 등으로 설정하는 경우 유용합니다.

메소드로 'POSTAPAPAP'을 전달하더라도 오류가 발생하지 않습니다.

'fdggdgilfdghfldj'를 formdata로 전달하더라도 오류가 발생하지 않습니다.

첫 번째 경우에는 오류가 내부 displayAjax() 아래 this.statusText 으로 Method not Allowed .

두 번째 경우에는 간단하게 작동합니다. 올바른 게시물 데이터를 전달했는지 서버 측에서 확인해야합니다.

도메인 간 허용되지 않으면 오류가 자동으로 발생합니다.

오류 응답에는 오류 코드가 없습니다.

error.로 설정된 this.type 만 있습니다 .

오류를 완전히 제어 할 수없는 경우 왜 오류 처리기를 추가합니까? 콜백 함수 displayAjax() 에서 대부분의 오류가이 안에 반환됩니다 .

따라서 URL을 올바르게 복사하여 붙여 넣을 수 있으면 오류를 확인할 필요가 없습니다. ;)

추신 : 첫 번째 테스트로 x ( 'x', displayAjax) ...를 작성했으며 완전히 응답했습니다 ... ??? 그래서 HTML이있는 폴더를 확인했는데 'x.xml'이라는 파일이있었습니다. 따라서 파일의 확장자를 잊어 버린 경우에도 XMLHttpRequest 2 WILL FIND IT . 나는 LOL했다


동 기적으로 파일 읽기

그렇게하지 마십시오.

잠시 동안 브라우저를 차단하려면 멋진 큰 .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를 특수 함수로 바꿉니다.


위의 기능은 기본 용도입니다.

기능을 확장하려면 ...

그래 넌 할수있어.

나는 많은 API를 사용하고 있으며 모든 HTML 페이지에 통합하는 첫 번째 기능 중 하나는이 답변의 첫 번째 Ajax 함수이며 GET 만 사용합니다 ...

그러나 XMLHttpRequest 2로 많은 작업을 수행 할 수 있습니다.

다운로드 관리자 (이력서, 파일 판독기, 파일 시스템으로 양쪽에 범위 사용), 캔버스를 사용하는 다양한 이미지 리사이 저 변환기, base64image 등으로 웹 SQL 데이터베이스 채우기 등 ... 그러나이 경우에만 해당 기능을 만들어야합니다 목적 ... 때로는 BLOB, 배열 버퍼가 필요하며 헤더를 설정하고 mimetype을 재정의 할 수 있으며 훨씬 더 많이 있습니다 ...

그러나 여기서 질문은 Ajax 응답을 반환하는 방법입니다 ... (나는 쉬운 방법을 추가했습니다.)




Answer 4 Benjamin Gruenbaum


약속을 사용하는 경우이 답변이 도움이됩니다.

이는 AngularJS, jQuery (지연된), 기본 XHR 대체 (페치), EmberJS, BackboneJS의 저장 또는 약속을 반환하는 모든 노드 라이브러리를 의미합니다.

코드는 다음과 같이 표시되어야합니다.

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.

Felix Kling은 jQuery를 AJAX 콜백과 함께 사용하는 사람들을 위해 훌륭한 답변을 작성했습니다. 네이티브 XHR에 대한 답변이 있습니다. 이 답변은 프론트 엔드 또는 백엔드에서 일반적인 약속 사용에 대한 것입니다.


핵심 이슈

NodeJS / io.js를 사용하는 브라우저 및 서버의 JavaScript 동시성 모델은 비동기식 이며 반응성이 있습니다.

당신은 약속, 수익하는 메서드를 호출 할 때마다 then 핸들러가되어 항상 이다 -로 비동기 적으로 실행 A의 아닌 그 아래의 코드 .then 핸들러를.

당신이 반환 할 때이 방법은 datathen 아직 실행하지 못했습니다 정의한 핸들러입니다. 이는 반환하는 값이 올바른 시간 값으로 설정되지 않았 음을 의미합니다.

다음은이 문제에 대한 간단한 비유입니다.

    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 부분이 아직 실행하지 않았다. 아마도 1 초 안에 실행될 것이지만, 그때까지는 반환 값과 관련이 없습니다.

작업이 아직 발생하지 않았기 때문에 (AJAX, 서버 호출, IO, 타이머) 요청이 코드에 해당 값이 무엇인지 알려주기 전에 값을 반환합니다.

이 문제에 대한 한 가지 가능한 해결책은 재활 성 코드를 작성 하여 계산이 완료되면 프로그램에 지시합니다. 약속은 본질적으로 시간적 (시간에 민감 함)이어서 적극적으로 가능하게합니다.

약속에 대한 빠른 요약

약속은 시간이 지남에 따른 가치 입니다. 약속에는 상태가 있으며 값없이 보류 중으로 시작하여 다음과 같이 해결할 수 있습니다.

  • 계산이 성공적으로 완료 되었음을 의미합니다.
  • 거부 계산이 실패했음을 의미한다.

약속은 한 번만 상태를 변경할 수 있으며 그 후에는 항상 같은 상태를 영원히 유지합니다. then 처리기를 연결 하여 값을 추출하고 오류를 처리 할 수 ​​있습니다. then 처리기 는 호출 체인 을 허용 합니다. 약속은이 를 반환하는 API를 사용하여 만들어집니다 . 예를 들어, 최신 AJAX 대체 fetch 또는 jQuery의 $.get 리턴 약속.

우리가 호출하면 .then 약속과에 반환 그것에서 무엇인가 - 우리의 약속을받을 처리 된 값을 . 우리가 다른 약속을 돌려 주면 놀라운 일을하게되지만 말을 붙잡아 봅시다.

약속으로

약속으로 위의 문제를 어떻게 해결할 수 있는지 봅시다. 먼저 지연 함수를 만들기 위해 Promise 생성자 를 사용하여 위에서 약속 상태에 대한 이해를 보여 드리겠습니다 .

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

이 메소드는 다른 코 루틴에서 사용할 수있는 promise 자체를 반환합니다. 예를 들면 다음과 같습니다.

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 수 있습니다 . 이것은 asyncawait 키워드를 추가하여 위의 ES6 제안서에 대한 "설탕"(더 구문)입니다 . 위의 예제를 작성하십시오.

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
}

그것은 여전히 ​​똑같은 약속을 반환합니다 :)




Answer 5 Nic


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

제출 핸들러에서 아무것도 리턴하지 않으면 아무 것도 수행되지 않습니다. 대신 데이터를 전달하거나 성공 함수 내에서 직접 원하는 데이터를 수행해야합니다.