javascript 순서 - 비동기 호출의 응답을 어떻게 반환합니까?




순차 처리 (25)

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`.

Answers

성공 의 callback()함수를 사용하십시오 foo(). 이 방법으로 시도하십시오. 이해하기 쉽고 간단합니다.

var lat = "";
var lon = "";
function callback(data) {
    lat = data.lat;
    lon = data.lon;
}
function getLoc() {
    var url = "http://ip-api.com/json"
    $.getJSON(url, function(data) {
        callback(data);
    });
}

getLoc();

질문은 :

비동기 호출의 응답을 어떻게 반환합니까?

이것은 다음과 같이 해석 될 수 있습니다 :

비동기 코드를 동기식 으로 만드는 방법은 무엇입니까?

해결책은 콜백을 피하고 Promisesasync / await를 함께 사용하는 것입니다 .

Ajax 요청에 대한 예제를 제공하고자한다.

(비록 자바 스크립트로 작성 될 수 있지만 파이썬으로 작성하고 Transcrypt 사용하여 Javascript로 컴파일하는 것이 더 좋을 것입니다.)

먼저 JQuery 사용을 활성화 $하여 S다음 과 같이 사용할 수있게합니다 .

__pragma__ ('alias', 'S', '$')

Promise 를 반환하는 함수를 정의하십시오. 이 경우에는 Ajax 호출입니다.

def read(url: str):
    deferred = S.Deferred()
    S.ajax({'type': "POST", 'url': url, 'data': { },
        'success': lambda d: deferred.resolve(d),
        'error': lambda e: deferred.reject(e)
    })
    return deferred.promise()

사용 비동기 그 것처럼 코드를 동기 :

async def readALot():
    try:
        result1 = await read("url_1")
        result2 = await read("url_2")
    except Exception:
        console.warn("Reading a lot failed")

짧은 대답은 다음과 같이 콜백을 구현해야한다는 것입니다.

function callback(response) {
    // Here you can do what ever you want with the response object.
    console.log(response);
}

$.ajax({
    url: "...",
    success: callback
});

코드에서 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 는 즉시 반환하고 다음 .send return .send 반환합니다 return result; , success 콜백으로 전달 된 함수가 호출되기도 전에 실행됩니다.

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

여기에 간단한 비유가있다.

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

(Fiddle)

a=5 부분이 아직 실행되지 않았으므로 반환 값은 undefined 않습니다. 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"를 알려야합니다. (Fiddle) .

가능한 해결책

기본적으로이 문제를 해결하는 방법에는 두 가지가 있습니다.

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

이런 종류의 콜백 설계가 어떻게 이루어 졌는지에 대한 자세한 내용은 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이 아니며 그에 따라 작동하는지 (fail handler를 생성하는 등) 확인하여 더 확장 할 수 있습니다. 효과적으로 문제를 해결합니다.

아직 이해가 안되면 MDN에서 AJAX Getting Getting Guide읽으십시오 .


약속을 사용하고 있다면,이 대답은 당신을위한 것입니다.

즉, 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은 AJAX에 대한 콜백을 사용하여 jQuery를 사용하는 사람들에게 답변을 작성하는 훌륭한 일을했습니다. 네이티브 XHR에 대한 답변이 있습니다. 이 대답은 프론트 엔드 또는 백엔드에서 약속을 일반적으로 사용하기위한 것입니다.

핵심 이슈

브라우저와 NodeJS / io.js가있는 서버의 JavaScript 동시성 모델은 비동기식 입니다.

약속을 반환하는 메서드를 호출 할 때마다 then 처리기는 항상 비동기 적으로 실행됩니다. .then 처리기에없는 코드가 처리 된 후입니다.

즉, 정의한 처리기가 아직 실행하지 않은 data 반환 할 때입니다. 이것은 반환하는 값이 올바른 값으로 설정되지 않았 음을 의미합니다.

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

    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 = 5 부분이 아직 실행되지 않았으므로 data 의 값은 undefined 않습니다. 그것은 잠시 후에 실행될 것입니다 만, 그때까지는 리턴 된 값과 무관합니다.

작업이 아직 (AJAX, 서버 호출, IO, 타이머) 발생하지 않았기 때문에 요청 전에 요청한 값이 무엇인지 코드에 알릴 수 있습니다.

이 문제를 해결할 수있는 한 가지 해결책은 계산이 완료되었을 때 프로그램이 무엇을해야하는지 다시 알리는 것입니다. 약속은 본질적으로 시간적 (시간에 민감) 됨으로써 적극적으로 이것을 가능하게합니다.

약속에 대한 간략한 요약

약속은 시간이 지남에 따라 가치가 있습니다 . 약속에는 상태가 있으며, 아무런 보류 상태로 시작하지 않고 정착 할 수 있습니다.

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

약속은 오직 한 번 상태를 바꿀 수 있습니다. 그 후에는 항상 같은 상태로 영원히 머 무르지 않을 것입니다. 핸들러를 연결하여 값을 추출하고 오류를 처리하겠다는 약속에 연결할 수 있습니다. 핸들러는 호출 chaining 을 허용합니다. 약속은 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 열 수있는 상자와 같습니다.

적용

원래 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 는 중간에 돌아올 수있는 기능인 generators 를 도입 한 다음 그들이 있던 시점을 재개합니다. 일반적으로 시퀀스에 유용합니다 (예 :

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,..... 이것은 독자적으로 흥미롭고 많은 가능성을위한 여지를 열어주는 반면, 흥미로운 한 가지 특별한 경우가 있습니다.

생성하는 시퀀스가 ​​숫자가 아닌 일련의 동작 인 경우, 함수가 반환 될 때마다 함수를 일시 중지하고 함수를 다시 시작하기 전에 함수를 기다릴 수 있습니다. 따라서 일련의 숫자 대신에 우리는 미래의 가치 의 연속을 필요로합니다 . 즉 약속입니다.

이 다소 까다 롭지 만 매우 강력한 트릭을 사용하면 동기식으로 비동기 코드를 작성할 수 있습니다. 이 작업을 수행하는 여러 "주자"가 있습니다. 하나는 코드의 짧은 몇 줄이지만이 대답의 범위를 벗어납니다. 나는 블루 버드의 사용됩니다 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
});

이 방법은 다른 coroutine에서 소비 할 수있는 약속 자체를 반환합니다. 예 :

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 제안에 asyncand와 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
}

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


Promise를 사용하여 작성된이 사용자 정의 라이브러리를 사용하여 원격 호출을 할 수 있습니다.

function $http(apiConfig) {
    return new Promise(function (resolve, reject) {
        var client = new XMLHttpRequest();
        client.open(apiConfig.method, apiConfig.url);
        client.send();
        client.onload = function () {
            if (this.status >= 200 && this.status < 300) {
                // Performs the function "resolve" when this.status is equal to 2xx.
                // Your logic here.
                resolve(this.response);
            }
            else {
                // Performs the function "reject" when this.status is different than 2xx.
                reject(this.statusText);
            }
        };
        client.onerror = function () {
            reject(this.statusText);
        };
    });
}

간단한 사용 예 :

$http({
    method: 'get',
    url: 'google.com'
}).then(function(response) {
    console.log(response);
}, function(error) {
    console.log(error)
});

짧은 대답 : 함수가 반환 된 후 호출이 비동기 적으로 실행 foo()되는 동안 메서드가 즉시 반환 됩니다. 문제는 비동기 호출이 반환 한 후에 검색된 결과를 저장하는 방법 또는 위치입니다.$ajax()

이 스레드에는 여러 가지 솔루션이 제공됩니다. 아마도 가장 쉬운 방법은 foo()메서드에 개체를 전달 하고 비동기 호출이 완료된 후 해당 개체의 멤버에 결과를 저장하는 것입니다.

function foo(result) {
    $.ajax({
        url: '...',
        success: function(response) {
            result.response = response;   // Store the async result
        }
    });
}

var result = { response: null };   // Object to hold the async result
foo(result);                       // Returns before the async completes

이 호출 foo()은 여전히 ​​아무런 도움이되지 않습니다. 그러나 비동기 호출의 결과는에 저장됩니다 result.response.


이것은 많은 새로운 JavaScript 프레임 워크에서 사용 되는 두 가지 방식의 데이터 바인딩 이 당신을 위해 크게 작용할 장소 중 하나입니다 ...

따라서 Angular, React 또는 두 가지 방식의 데이터 바인딩 을 수행하는 다른 프레임 워크를 사용하는 경우이 문제는 간단하게 해결되므로 간단한 결과로 결과가 undefined첫 번째 단계에 있으므로 result = undefined데이터를 받기 전에 얻은 결과입니다 . 그러면 결과를 얻 자마자 업데이트되고 Ajax 호출에 응답하는 새 값에 할당됩니다.

그러나 당신 이이 질문에서 물었던 것처럼 예를 들어 순수한 자바 스크립트 또는 jQuery 에서 어떻게 할 수 있습니까?

콜백 , 약속 및 최근에 관찰 할 수있는 것을 사용하여 success () 또는 then ()과 같은 기능을 제공하는 약속과 같이 콜백 또는 가입 함수 와 마찬가지로 데이터가 준비 될 때 실행됩니다. 에 관찰 .

예를 들어 jQuery 를 사용중인 경우 다음과 같이 할 수 있습니다.

$(document).ready(function(){
    function foo() {
        $.ajax({url: "api/data", success: function(data){
            fooDone(data); //after we have data, we pass it to fooDone
        }});
    };

    function fooDone(data) {
        console.log(data); //fooDone has the data and console.log it
    };

    foo(); //call happens here
});

이 비동기 작업을 수행하는 새로운 방법 인 약속관찰 사항에 대해 자세히 알아 보려면


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

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


코드를 던지기보다는 JS가 콜백 및 비동기를 처리하는 방법을 이해하는 데 중요한 두 가지 개념이 있습니다. (심지어 단어입니까?)

이벤트 루프 및 동시성 모델

당신이 알아야 할 세 가지가 있습니다. 큐. 이벤트 루프 및 스택

광범위하고 단순한 용어로, 이벤트 루프는 프로젝트 관리자와 같으며 큐와 스택간에 실행하고 통신하려는 모든 기능을 지속적으로 수신합니다.

while (queue.waitForMessage()) {
   queue.processNextMessage();
}

일단 메시지를 수신하면 대기열에 메시지를 추가합니다. 대기열은 AJAX 요청과 같이 실행 대기중인 항목 목록입니다. 이것을 상상해 보라.

 1. call foo.com/api/bar using foobarFunc
 2. Go perform an infinite loop
 ... and so on

이 메시지 중 하나가 실행될 때 메시지를 대기열에서 팝하고 스택을 생성하면 스택은 메시지에서 명령을 수행하기 위해 JS가 실행해야하는 모든 것입니다. 그래서 우리의 예에서는 전화를 걸라고 들었습니다.foobarFunc

function foobarFunc (var) {
  console.log(anotherFunction(var));
}

그래서 foobarFunc가 (우리의 경우 anotherFunction) 실행해야하는 것은 스택에 푸시됩니다. 실행 된 후 잊어 버리면 이벤트 루프가 대기열의 다음 항목으로 이동합니다 (또는 메시지 수신 대기).

여기에서 핵심은 실행 순서입니다. 그건

언제 무언가가 움직일 것인가?

AJAX를 사용하여 외부 상대방에게 전화를 걸거나 비동기 코드 (예 : setTimeout)를 실행하면 Javascript가 응답에 의존하여 진행될 수 있습니다.

가장 큰 문제는 언제 응답을 얻을 것인가? 그 대답은 우리가 모른다는 것입니다. 그래서 이벤트 루프는 "저를 움직여주십시오"라는 메시지를 기다리고 있습니다. JS가 해당 메시지를 동 기적으로 기다렸다가 앱이 멈 추면 빨아 들일 것입니다. 그래서 JS는 메시지가 대기열에 다시 추가 될 때까지 기다리는 동안 대기열에서 다음 항목을 계속 실행합니다.

그렇기 때문에 비동기식 기능으로 콜백 (callback) 이라는 것을 사용 합니다. 그것은 다소 문자 그대로 then() 과 같습니다 . 어떤 시점에서 뭔가를 반환하겠다고 약속 한 것처럼 jQuery는 ( deffered.done deffered.faildeffered.always다른 것 중에서) 호출 된 특정 콜백을 사용합니다 . here 모두 볼 수 here

따라서 당신이해야 할 일은 어떤 시점에서 실행되도록 약속 된 함수를 전달하는 것입니다.

콜백은 즉시 실행되지 않지만 나중에 실행 된 함수에 대한 참조를 전달하는 것이 중요합니다. 그래서

function foo(bla) {
  console.log(bla)
}

그래서 대부분의 시간 (그러나 항상 그런 것은 아닙니다) 당신은지나 가지 foo않을 것입니다.foo()

희망적으로 그것은 약간 이해 될 것이다. 이처럼 혼란스러워 보이는 것들을 만났을 때 - 나는 적어도 그것을 이해할 수 있도록 문서를 완전히 읽는 것이 좋습니다. 그것은 당신을 훨씬 더 나은 개발자로 만들 것입니다.


물론 동기식 요청, 약속과 같은 많은 접근 방식이 있지만 내 경험에 비추어 볼 때 콜백 방식을 사용해야한다고 생각합니다. Javascript의 비동기 동작에는 자연 스럽습니다. 따라서 코드 스 니펫은 약간 다른 코드로 다시 작성할 수 있습니다.

function foo() {
    var result;

    $.ajax({
        url: '...',
        success: function(response) {
            myCallback(response);
        }
    });

    return result;
}

function myCallback(response) {
    // Does something.
}

ES2017을 사용하면 이것을 함수 선언으로 가져야합니다.

async function foo() {
    var response = await $.ajax({url: '...'})
    return response;
}

그리고 이렇게 실행하십시오.

(async function() {
    try {
        var result = await foo()
        console.log(result)
    } catch (e) {}
})()

또는 Promise 구문

foo().then(response => {
    console.log(response)

}).catch(error => {
    console.log(error)

})

Js는 단일 스레드입니다.

브라우저는 세 부분으로 나눌 수 있습니다.

1) 이벤트 루프

2) 웹 API

3) 이벤트 대기열

이벤트 루프는 영원히 실행됩니다. 즉, 무한 루프의 종류입니다. 이벤트 큐는 모든 이벤트가 어떤 이벤트 (예 : 클릭)에 푸시되는 곳입니다.이 이벤트는 큐 하나에서 수행되고이 함수를 실행하는 이벤트 루프에 넣고 스스로 준비합니다. 첫번째 함수가 실행 된 후 다음에 실행됩니다. 즉, 하나의 함수 실행은 큐에있는 함수가 이벤트 루프에서 실행될 때까지 시작되지 않습니다.

이제 큐에서 두 개의 함수를 밀어 넣었다고 생각해 봅시다. 서버에서 데이터를 가져오고 다른 데이터를 사용하는 함수를 사용합니다. 먼저 queue에서 serverRequest () 함수를 밀고 utiliseData () 함수를 보았습니다. serverRequest 함수는 이벤트 루프에 들어가서 서버에서 데이터를 가져 오는 데 얼마나 많은 시간이 걸릴지 모르기 때문에이 프로세스에 시간이 걸릴 것으로 예상되므로 이벤트 루프가 페이지를 걸기 때문에 바쁘다. API는 이벤트 루프에서이 함수를 가져오고 이벤트 루프를 자유롭게 만드는 서버를 다루므로 큐에서 다음 함수를 실행할 수 있습니다. 큐의 다음 함수는 루프에 들어가는 utiliseData ()이지만 사용할 수있는 데이터가 없으므로 utiliseData 다음 함수의 낭비 및 실행은 대기열의 끝까지 계속됩니다. (이것은 비동기 호출이라고합니다. 즉 데이터를 얻을 때까지 다른 작업을 수행 할 수 있습니다)

serverRequest () 함수가 코드에서 return 문을 가지고 있다고 가정 해 봅시다. 서버 Web API에서 데이터를 가져 오면 큐의 끝에있는 큐에이를 밀어 넣습니다. 대기열에 마지막으로 밀어 넣기 때문에이 데이터를 활용하기 위해 대기열에 남아있는 기능이 없기 때문에 데이터를 활용할 수 없습니다. 따라서 비동기 호출에서 어떤 것을 반환 할 수 없습니다.

따라서 이것에 대한 해결책은 콜백 또는 약속 입니다.

여기에있는 답변 중 하나의 이미지, 콜백 사용에 대한 설명 ... 서버에서 반환 한 데이터를 사용하는 함수를 함수 호출 서버에 제공합니다.

 function doAjax(callbackFunc, method, url) {
  var xmlHttpReq = new XMLHttpRequest();
  xmlHttpReq.open(method, url);
  xmlHttpReq.onreadystatechange = function() {

      if (xmlHttpReq.readyState == 4 && xmlHttpReq.status == 200) {
        callbackFunc(xmlHttpReq.responseText);
      }


  }
  xmlHttpReq.send(null);

}

내 코드에서는 다음과 같이 호출됩니다.

function loadMyJson(categoryValue){
  if(categoryValue==="veg")
  doAjax(print,"GET","http://localhost:3004/vegetables");
  else if(categoryValue==="fruits")
  doAjax(print,"GET","http://localhost:3004/fruits");
  else 
  console.log("Data not found");
}

비동기 호출을 만들기위한 ECMA (2016/17)의 새로운 메소드를 보려면 여기를 읽으십시오 (@Felix Kling Answer on Top) https://.com/a/14220323/7579856


비동기 함수에서 값을 반환하는 또 다른 방법은 비동기 함수의 결과를 저장할 객체를 전달하는 것입니다.

다음은 같은 예입니다.

var async = require("async");

// This wires up result back to the caller
var result = {};
var asyncTasks = [];
asyncTasks.push(function(_callback){
    // some asynchronous operation
    $.ajax({
        url: '...',
        success: function(response) {
            result.response = response;
            _callback();
        }
    });
});

async.parallel(asyncTasks, function(){
    // result is available after performing asynchronous operation
    console.log(result)
    console.log('Done');
});

내가 사용하고 result비동기 작업 중에 값을 저장하는 객체. 따라서 비동기 작업 후에도 결과를 사용할 수 있습니다.

나는이 접근법을 많이 사용한다. 연속적인 모듈을 통해 결과를 연결하는 것이 얼마나 효과적인지 알고 싶습니다.


→ 다른 예제로 비동기 동작에 대한 일반적인 설명을 보려면 함수 내에서 변수를 수정 한 후 내 변수가 변경되지 않은 이유 를 참조하십시오 . - 비동기 코드 참조

→ 문제를 이미 이해 한 경우 아래의 가능한 해결 방법으로 건너 뜁니다.

문제

AjaxAasynchronous 나타냅니다. 이는 요청을 보내는 (또는 오히려 응답을받는) 것이 정상적인 실행 흐름에서 벗어 났음을 의미합니다. 귀하의 예제에서 $.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 호출이 끝난 후 명령문이 실행됩니다. 응답을 얻으려면 응답을받은 후에 호출 할 함수 ( 콜백 ( call back) ?)를 제공해야합니다. 콜백이 호출되기 전에 호출이 실행됩니다.

솔루션

자바 스크립트의 비동기 성격을 포용하십시오! 특정 비동기 작업은 동기 대응 물을 제공하지만 ( "Ajax"도 마찬가지 임) 일반적으로 브라우저 컨텍스트에서 사용하는 것이 일반적으로 권장되지 않습니다.

왜 그렇게 나쁘지?

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

이 모든 것은 정말 나쁜 사용자 경험입니다. 사용자는 모든 것이 잘 작동하는지 여부를 알 수 없습니다. 또한 연결 속도가 느린 사용자에게는 그 영향이 더 심합니다.

다음에서 우리는 모두 서로 위에 쌓여있는 세 가지 솔루션을 살펴볼 것입니다.

  • async/await (Promiler with async/await (변환기 또는 재생기를 사용하는 경우 구형 브라우저에서 사용할 수있는 ES2017 +)
  • 콜백 (노드에서 널리 사용됨 )
  • then() (ES2015 +, 많은 약속 라이브러리 중 하나를 사용하는 경우 구형 브라우저에서 사용 가능)

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

ES2017 + : async/await 약속

2017 년에 발표 된 ECMAScript 버전은 비동기 함수에 대한 구문 수준 지원 을 도입했습니다. asyncawait 의 도움으로 async "동기식 스타일"로 작성할 수 있습니다. 코드는 여전히 비동기이지만 읽기 / 이해하기가 쉽습니다.

async/await 은 약속 위에 구축됩니다 : async 함수는 항상 약속을 반환합니다. 약속을 "언랩 (unwrap)"하고 약속이 해결 된 값을 가져 오거나 약속이 거부되면 오류를 던집니다.

중요 사항 : async 기능에서만 await 사용할 수 있습니다. 즉, 최상위 수준에서 약속을 직접 수행해야합니다.

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

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

함수가 콜백을 허용하도록 함

콜백은 단순히 다른 함수로 전달되는 함수입니다. 그 다른 함수는 준비 될 때마다 전달 된 함수를 호출 할 수 있습니다. 비동기 프로세스의 컨텍스트에서 비동기 프로세스가 완료 될 때마다 콜백이 호출됩니다. 일반적으로 결과는 콜백으로 전달됩니다.

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

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

된다

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 을 호출하고 응답을 callback 에 전달합니다 (콜백을 정의한 방식이므로 result 로 참조 할 수 있음).

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

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

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

ES2015 + : then with then()

then() 는 ECMAScript 6 (ES2015)의 새로운 기능이지만 이미 훌륭한 브라우저 지원 기능 을 제공합니다. 표준 Promises 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 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
}

이 코드는 위의 비동기 문제를 오해합니다. 특히, 서버에서 '/ password'페이지를 확인하는 동안 $.ajax() 가 코드를 고정하지 않습니다. 서버에 요청을 보내면 기다리는 동안 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는 본질적으로 항상 비동기 적입니다 (이 옵션을 고려조차하지 않는 또 하나의 이유).


다음 예제를 살펴보십시오.

var app = angular.module('plunker', []);

app.controller('MainCtrl', function($scope,$http) {

    var getJoke = function(){
        return $http.get('http://api.icndb.com/jokes/random').then(function(res){
            return res.data.value;  
        });
    }

    getJoke().then(function(res) {
        console.log(res.joke);
    });
});

볼 수 있듯이 해결 된 약속getJoke반환 됩니다 (반환 할 때 해결됨 ). 따라서 $ http.get 요청이 완료 될 때까지 기다린 다음 console.log (res.joke) 가 실행됩니다 (일반적인 비동기식 플로우처럼).res.data.value

이것은 plnkr입니다.

http://embed.plnkr.co/XlNR7HpCaIhJxskMJfSg/


우리는 우리가 "시간"이라고 부르는 차원을 따라 진행되는 것처럼 보이는 우주에서 우리 자신을 발견합니다. 우리는 실제로 어떤 시간인지 이해하지 못합니다. 그러나 우리는 "과거", "현재", "미래", "이전", "이후"등 우리가 추론하고 그것에 대해 이야기하게하는 추상적 개념과 어휘를 개발했습니다.

우리가 구축하는 컴퓨터 시스템은 시간이 중요한 차원입니다. 미래에 어떤 일이 일어나기 마련입니다. 그런 다음 첫 번째 일이 결국 발생하면 다른 일이 일어날 필요가 있습니다. 이것이 "비동기 성"이라는 기본 개념입니다. 점점 네트워크화 된 세계에서 가장 일반적인 비동기 성은 원격 시스템이 일부 요청에 대한 응답을 기다리는 것입니다.

예를 들어 보겠습니다.우유 배달원에게 전화해서 우유를 주문하십시오. 그것이 올 때, 당신은 당신의 커피에 그것을 넣고 싶습니다. 커피가 아직 없기 때문에 우유에 넣을 수 없습니다. 그것을 커피에 넣기 전에 기다려야합니다. 즉, 다음은 작동하지 않습니다.

var milk = order_milk();
put_in_coffee(milk);

JS는 필요가 있음을 알 수있는 방법이 없기 때문에 대기 위해 order_milk자신을 실행하기 전에 완료를 put_in_coffee. 즉, 그 모르는 order_milk입니다 비동기은 미래의 어느 때까지 우유를 초래하지 않을 무언가를 --is. JS 및 기타 선언적 언어는 기다리지 않고 하나의 명령문을 실행합니다.

이 문제에 대한 JS의 접근 방식은 JS가 전달할 수있는 일류 객체로 함수를 지원한다는 사실을 이용하여 함수를 asynchonous 요청에 대한 매개 변수로 전달합니다.이 함수는 완료 될 때 호출됩니다 언젠가는 미래에 그 임무를 수행해야합니다. 이것이 바로 "콜백 (callback)"접근 방식입니다. 다음과 같이 보입니다.

order_milk(put_in_coffee);

order_milk시작하고 우유를 주문한 다음 도착할 때만 호출합니다 put_in_coffee.

이 콜백 접근법의 문제점은 함수의 결과를보고하는 함수의 일반적인 의미를 오염 시킨다는 것입니다 return. 대신 함수는 매개 변수로 주어진 콜백을 호출하여 결과를보고해야합니다. 또한,이 접근법은 긴 사건 순서를 다룰 때 다루기 힘들어 질 수 있습니다. 예를 들어, 우유에 커피를 넣고 커피를 마시는 것을 기다리고 싶다고 가정 해 봅시다. 나는 이런 식으로 쓸 필요가있다.

order_milk(function(milk) { put_in_coffee(milk, drink_coffee); }

I가 통과하고 곳 put_in_coffee도 작업을 그 안에 넣어 우유 모두, 그리고 ( drink_coffee)을 우유에 넣어되면 이러한 코드를 작성 어려워진다. 실행, 읽기, 디버깅한다.

이 경우 질문의 코드를 다음과 같이 다시 작성할 수 있습니다.

var answer;
$.ajax('/foo.json') . done(function(response) {
  callback(response.data);
});

function callback(data) {
  console.log(data);
}

약속 입력

이것은 어떤 약속의 미래 또는 비동기 결과 를 나타내는 특정한 유형의 "약속"이라는 개념에 대한 동기였습니다 . 이미 일어난 일, 미래에 일어날 일, 또는 전혀 일어나지 않을 일을 나타낼 수 있습니다. 약속에는 then약속이 나타내는 결과가 실현되었을 때 실행될 작업을 전달 하는 단일 메서드 가 있습니다.

우리 우유와 커피의 경우, 우리는 설계 order_milk후, 도착 우유에 대한 약속을 반환 지정하는 put_in_coffeeA와 then다음과 같이 조치 :

order_milk() . then(put_in_coffee)

이것의 장점 중 하나는이 문자열을 함께 묶어 향후 발생 시퀀스를 만드는 것입니다 ( "체인화").

order_milk() . then(put_in_coffee) . then(drink_coffee)

당신의 특별한 문제에 대한 약속을 적용합시다. 우리는 우리의 요청 로직을 함수 안에 담아서 약속을 되 돌린다.

function get_data() {
  return $.ajax('/foo.json');
}

실제로, 우리가했던 모든 return것은에 호출에 추가 됩니다 $.ajax. 이것은 jQuery가 $.ajax이미 일종의 약속과 비슷한 것을 반환 하기 때문에 가능 합니다. (실제적으로, 세부 사항에 빠지지 않고, 실제 약속을 되 돌리거나이 대안을 사용할 수 있도록이 호출을 감싸는 것을 선호합니다 $.ajax.) 이제 파일을로드하고 완료 될 때까지 기다렸다가 뭔가를해라, 우리는 간단하게 말할 수있다.

get_data() . then(do_something)

예를 들어,

get_data() . 
  then(function(data) { console.log(data); });

약속을 사용할 때 많은 함수가 전달 then되기 때문에보다 컴팩트 한 ES6 스타일의 화살표 함수를 사용하는 것이 도움이됩니다.

get_data() . 
  then(data => console.log(data));

async키워드

그러나 동기식이라면 코드를 한 가지 방법으로 작성하고 비동기식이라면 코드를 작성하는 것에 대해 모호하게 불만이 있습니다. 동기식의 경우, 우리는

a();
b();

그러나 a비동기식 인 경우 , 우리는

a() . then(b);

위에서 JS는 "JS는 두 번째 실행 전에 첫 번째 호출이 끝날 때까지 기다릴 필요가 있음을 알 수 없습니다 ." JS에게 말하는 방법 있다면 좋지 않을까요 ? await"비동기"함수라는 특별한 유형의 함수 안에 사용되는 키워드 가 있음이 드러납니다 . 이 기능은 곧 출시 될 ES 버전의 일부이지만 올바른 사전 설정이 제공되는 Babel과 같은 변환기에서 이미 사용할 수 있습니다. 이를 통해 우리는

async function morning_routine() {
  var milk   = await order_milk();
  var coffee = await put_in_coffee(milk);
  await drink(coffee);
}

귀하의 경우에는 다음과 같이 작성할 수 있습니다.

async function foo() {
  data = await get_data();
  console.log(data);
}

대부분의 대답은 단일 비동기 작업이있을 때 유용한 제안을하지만 때로는 배열이나 다른 목록과 유사한 구조의 항목에 대해 비동기 작업을 수행해야 할 때 발생합니다 . 유혹은 이것을하는 것입니다 :

// WRONG
var results = [];
theArray.forEach(function(entry) {
    doSomethingAsync(entry, function(result) {
        results.push(result);
    });
});
console.log(results); // E.g., using them, returning them, etc.

예:

// WRONG
var theArray = [1, 2, 3];
var results = [];
theArray.forEach(function(entry) {
    doSomethingAsync(entry, function(result) {
        results.push(result);
    });
});
console.log("Results:", results); // E.g., using them, returning them, etc.

function doSomethingAsync(value, callback) {
    console.log("Starting async operation for " + value);
    setTimeout(function() {
        console.log("Completing async operation for " + value);
        callback(value * 2);
    }, Math.floor(Math.random() * 200));
}
.as-console-wrapper {
  max-height: 100% !important;
}

그 이유는 작동하지 않는 콜백 doSomethingAsync이 결과를 사용하려고 할 때 아직 실행되지 않았기 때문입니다.

따라서 배열 (또는 일종의 목록)이 있고 각 항목에 대해 비동기 작업을 수행하려는 경우 두 가지 옵션이 있습니다. 즉, 작업을 병렬로 (겹치기) 또는 직렬로 (차례로 차례로) 수행합니다.

평행

모든 콜백을 시작하고 예상 콜백 수를 추적하여 많은 콜백을 얻었을 때 결과를 사용할 수 있습니다.

var results = [];
var expecting = theArray.length;
theArray.forEach(function(entry, index) {
    doSomethingAsync(entry, function(result) {
        results[index] = result;
        if (--expecting === 0) {
            // Done!
            console.log("Results:", results); // E.g., using the results
        }
    });
});

예:

var theArray = [1, 2, 3];
var results = [];
var expecting = theArray.length;
theArray.forEach(function(entry, index) {
    doSomethingAsync(entry, function(result) {
        results[index] = result;
        if (--expecting === 0) {
            // Done!
            console.log("Results:", results); // E.g., using the results
        }
    });
});

function doSomethingAsync(value, callback) {
    console.log("Starting async operation for " + value);
    setTimeout(function() {
        console.log("Completing async operation for " + value);
        callback(value * 2);
    }, Math.floor(Math.random() * 200));
}
.as-console-wrapper {
  max-height: 100% !important;
}

(우리는 멀리 expecting하고 그냥 사용 results.length === theArray.length하지만, 그것은 theArray전화가 걸리는 동안 변경되는 가능성을 열어줍니다 ...)

우리가 사용하는 방법에 주목 index로부터 forEach의 결과를 저장하는 results결과 순서가 도착하더라도, 그것은 관련 항목과 같은 위치에서 (비동기 호출이 반드시 순서대로 완료하지 않기 때문에 그들은 시작된하는).

그러나 함수에서 결과 를 반환 해야하는 경우 어떻게해야 합니까? 다른 대답이 지적한대로, 당신은 할 수 없습니다; 함수를 수락하고 콜백을 호출해야합니다 (또는 then() 반환해야합니다 ). 다음은 콜백 버전입니다.

function doSomethingWith(theArray, callback) {
    var results = [];
    var expecting = theArray.length;
    theArray.forEach(function(entry, index) {
        doSomethingAsync(entry, function(result) {
            results[index] = result;
            if (--expecting === 0) {
                // Done!
                callback(results);
            }
        });
    });
}
doSomethingWith(theArray, function(results) {
    console.log("Results:", results);
});

예:

function doSomethingWith(theArray, callback) {
    var results = [];
    var expecting = theArray.length;
    theArray.forEach(function(entry, index) {
        doSomethingAsync(entry, function(result) {
            results[index] = result;
            if (--expecting === 0) {
                // Done!
                callback(results);
            }
        });
    });
}
doSomethingWith([1, 2, 3], function(results) {
    console.log("Results:", results);
});

function doSomethingAsync(value, callback) {
    console.log("Starting async operation for " + value);
    setTimeout(function() {
        console.log("Completing async operation for " + value);
        callback(value * 2);
    }, Math.floor(Math.random() * 200));
}
.as-console-wrapper {
  max-height: 100% !important;
}

또는 Promise대신 여기를 반환하는 버전이 있습니다 .

function doSomethingWith(theArray) {
    return new Promise(function(resolve) {
        var results = [];
        var expecting = theArray.length;
        theArray.forEach(function(entry, index) {
            doSomethingAsync(entry, function(result) {
                results[index] = result;
                if (--expecting === 0) {
                    // Done!
                    resolve(results);
                }
            });
        });
    });
}
doSomethingWith(theArray).then(function(results) {
    console.log("Results:", results);
});

물론 doSomethingAsync오류가 발생 reject하면 약속을 거부하는 데 사용 합니다.)

예:

function doSomethingWith(theArray) {
    return new Promise(function(resolve) {
        var results = [];
        var expecting = theArray.length;
        theArray.forEach(function(entry, index) {
            doSomethingAsync(entry, function(result) {
                results[index] = result;
                if (--expecting === 0) {
                    // Done!
                    resolve(results);
                }
            });
        });
    });
}
doSomethingWith([1, 2, 3]).then(function(results) {
    console.log("Results:", results);
});

function doSomethingAsync(value, callback) {
    console.log("Starting async operation for " + value);
    setTimeout(function() {
        console.log("Completing async operation for " + value);
        callback(value * 2);
    }, Math.floor(Math.random() * 200));
}
.as-console-wrapper {
  max-height: 100% !important;
}

(또는 교대 doSomethingAsync로 약속을 반환하는 래퍼를 만든 다음 아래에서 수행 할 수 있습니다 ...)

경우 doSomethingAsync당신에게주는 then() , 당신은 사용할 수 있습니다 Promise.all:

function doSomethingWith(theArray) {
    return Promise.all(theArray.map(function(entry) {
        return doSomethingAsync(entry, function(result) {
            results.push(result);
        });
    }));
}
doSomethingWith(theArray).then(function(results) {
    console.log("Results:", results);
});

예:

function doSomethingWith(theArray) {
    return Promise.all(theArray.map(function(entry) {
        return doSomethingAsync(entry, function(result) {
            results.push(result);
        });
    }));
}
doSomethingWith([1, 2, 3]).then(function(results) {
    console.log("Results:", results);
});

function doSomethingAsync(value) {
    console.log("Starting async operation for " + value);
    return new Promise(function(resolve) {
        setTimeout(function() {
            console.log("Completing async operation for " + value);
            resolve(value * 2);
        }, Math.floor(Math.random() * 200));
    });
}
.as-console-wrapper {
  max-height: 100% !important;
}

참고 Promise.all약속을들이 모두 해결 될 때 당신이 그것을주는 약속의 모든 결과의 배열과의 약속을 해결하거나 거부 한 경우에 첫번째 당신이 그것을 거부주는 약속.

시리즈

작업을 병렬로 수행하지 않으려한다고 가정합니다. 하나씩 차례대로 실행하려면 다음 작업을 시작하기 전에 각 작업이 완료 될 때까지 기다려야합니다. 다음은이를 수행하고 그 결과로 콜백을 호출하는 함수의 예입니다.

function doSomethingWith(theArray, callback) {
    var results = [];
    doOne(0);
    function doOne(index) {
        if (index < theArray.length) {
            doSomethingAsync(theArray[index], function(result) {
                results.push(result);
                doOne(index + 1);
            });
        } else {
            // Done!
            callback(results);
        }
    }
}
doSomethingWith(theArray, function(results) {
    console.log("Results:", results);
});

(우리가 일련의 작업을하고 있기 때문에, 우리는 results.push(result)순서대로 결과를 얻지 못할 것이라는 것을 알기 때문에 사용할 수 있습니다 . 위의 예에서는 사용할 수 results[index] = result;있었지만, 다음 예제에서는 인덱스가 없습니다 쓰다.)

예:

function doSomethingWith(theArray, callback) {
    var results = [];
    doOne(0);
    function doOne(index) {
        if (index < theArray.length) {
            doSomethingAsync(theArray[index], function(result) {
                results.push(result);
                doOne(index + 1);
            });
        } else {
            // Done!
            callback(results);
        }
    }
}
doSomethingWith([1, 2, 3], function(results) {
    console.log("Results:", results);
});

function doSomethingAsync(value, callback) {
    console.log("Starting async operation for " + value);
    setTimeout(function() {
        console.log("Completing async operation for " + value);
        callback(value * 2);
    }, Math.floor(Math.random() * 200));
}
.as-console-wrapper {
  max-height: 100% !important;
}

(또는 다시, 그 래퍼를 빌드 doSomethingAsync하면 약속을하고 아래에서 ...)

경우 doSomethingAsync당신에게 약속을 제공합니다 (같은 transpiler와 아마 ES2017 + 구문을 사용할 수있는 경우, 바벨 ), 당신은 사용할 수있는 async/await 함께 for-of하고 await:

async function doSomethingWith(theArray) {
    const results = [];
    for (const entry of theArray) {
        results.push(await doSomethingAsync(entry));
    }
    return results;
}
doSomethingWith(theArray).then(results => {
    console.log("Results:", results);
});

예:

async function doSomethingWith(theArray) {
    const results = [];
    for (const entry of theArray) {
        results.push(await doSomethingAsync(entry));
    }
    return results;
}
doSomethingWith([1, 2, 3]).then(function(results) {
    console.log("Results:", results);
});

function doSomethingAsync(value) {
    console.log("Starting async operation for " + value);
    return new Promise(function(resolve) {
        setTimeout(function() {
            console.log("Completing async operation for " + value);
            resolve(value * 2);
        }, Math.floor(Math.random() * 200));
    });
}
.as-console-wrapper {
  max-height: 100% !important;
}

(아직) ES2017 + 구문을 사용할 수 없다면, "Promise reduce"패턴 의 변형을 사용할 수 있습니다 (기존의 Promise reduce보다 더 복잡합니다. 왜냐하면 우리는 결과를 하나씩 전달하지 않기 때문에) 결과를 배열로 수집).

function doSomethingWith(theArray) {
    return theArray.reduce(function(p, entry) {
        return p.then(function(results) {
            return doSomethingAsync(entry).then(function(result) {
                results.push(result);
                return results;
            });
        });
    }, Promise.resolve([]));
}
doSomethingWith(theArray).then(function(results) {
    console.log("Results:", results);
});

예:

function doSomethingWith(theArray) {
    return theArray.reduce(function(p, entry) {
        return p.then(function(results) {
            return doSomethingAsync(entry).then(function(result) {
                results.push(result);
                return results;
            });
        });
    }, Promise.resolve([]));
}
doSomethingWith([1, 2, 3]).then(function(results) {
    console.log("Results:", results);
});

function doSomethingAsync(value) {
    console.log("Starting async operation for " + value);
    return new Promise(function(resolve) {
        setTimeout(function() {
            console.log("Completing async operation for " + value);
            resolve(value * 2);
        }, Math.floor(Math.random() * 200));
    });
}
.as-console-wrapper {
  max-height: 100% !important;
}

... ES2015 + 화살표 기능 이 덜 복잡 합니다 .

function doSomethingWith(theArray) {
    return theArray.reduce((p, entry) => p.then(results => doSomethingAsync(entry).then(result => {
        results.push(result);
        return results;
    })), Promise.resolve([]));
}
doSomethingWith(theArray).then(results => {
    console.log("Results:", results);
});

예:

function doSomethingWith(theArray) {
    return theArray.reduce((p, entry) => p.then(results => doSomethingAsync(entry).then(result => {
        results.push(result);
        return results;
    })), Promise.resolve([]));
}
doSomethingWith([1, 2, 3]).then(function(results) {
    console.log("Results:", results);
});

function doSomethingAsync(value) {
    console.log("Starting async operation for " + value);
    return new Promise(function(resolve) {
        setTimeout(function() {
            console.log("Completing async operation for " + value);
            resolve(value * 2);
        }, Math.floor(Math.random() * 200));
    });
}
.as-console-wrapper {
  max-height: 100% !important;
}


또 다른 해결책은 순차적 인 실행 프로그램 인 nsynjs 를 통해 코드를 실행하는 nsynjs 입니다.

기본 기능이 약속 된 경우

nsynjs는 모든 약속을 순차적으로 평가하고 약속 결과를 data속성에 넣습니다 .

function synchronousCode() {

    var getURL = function(url) {
        return window.fetch(url).data.text().data;
    };
    
    var url = 'https://ajax.googleapis.com/ajax/libs/jquery/2.0.0/jquery.min.js';
    console.log('received bytes:',getURL(url).length);
    
};

nsynjs.run(synchronousCode,{},function(){
    console.log('synchronousCode done');
});
<script src="https://rawgit.com/amaksr/nsynjs/master/nsynjs.js"></script>

기본 기능이 약속되어 있지 않은 경우

1 단계. 콜백 함수를 nsynjs-aware 래퍼에 랩핑합니다 (버전을 약속 한 경우이 테스트를 건너 뛸 수 있습니다).

var ajaxGet = function (ctx,url) {
    var res = {};
    var ex;
    $.ajax(url)
    .done(function (data) {
        res.data = data;
    })
    .fail(function(e) {
        ex = e;
    })
    .always(function() {
        ctx.resume(ex);
    });
    return res;
};
ajaxGet.nsynjsHasCallback = true;

2 단계. 동기식 로직을 함수에 넣기 :

function process() {
    console.log('got data:', ajaxGet(nsynjsCtx, "data/file1.json").data);
}

3 단계. nnsynjs를 통해 동기식 방식으로 함수를 실행합니다.

nsynjs.run(process,this,function () {
    console.log("synchronous function finished");
});

Nsynjs는 모든 연산자와 표현식을 단계별로 평가하여 느린 함수의 결과가 준비되지 않은 경우 실행을 일시 중지합니다.

여기에 더 많은 예제가 있습니다 : https://github.com/amaksr/nsynjs/tree/master/examples


나무를보기 전에 먼저 숲을 보자.

훌륭한 정보가 담긴 많은 유익한 답변이 여기에 있습니다. 나는 그 중 아무 것도 반복하지 않을 것입니다. 자바 스크립트 프로그래밍의 핵심은 먼저 전체 실행에 대한 올바른 정신 모형 을 갖는 것 입니다.

  1. 진입 점은 이벤트의 결과로 실행됩니다. 예를 들어 코드가있는 스크립트 태그가 브라우저에로드됩니다. 따라서 DOM 요소가 먼저 생성되어야하는 경우 코드를 실행하기 위해 페이지 준비에 관심을 기울여야 할 수도 있습니다.
  2. 코드는 실행을 완료 한 - 그러나 많은 비동기 호출을가한다 - 실행하지 않고 있는 등 시간 제한, DOM 이벤트 핸들러, 대기열에 앉을 실행되기 위해 대기하는 콜백의 각을 설정 XHR 요청을 포함하여 콜백,의를, 대기 해고 된 다른 사건이 끝나면 그 차례가 끝나고 모든 실행이 끝났습니다.
  3. XHR 요청에 대한 각각의 개별 콜백, 한 번 호출 된 이벤트의 timeout 또는 dom 설정은 완료 될 때까지 실행됩니다.

좋은 소식은이 점을 잘 이해하면 경쟁 조건에 대해 걱정할 필요가 없다는 것입니다. 먼저 코드를 서로 다른 이산 이벤트에 대한 응답으로 구성하려는 방법과 논리적 순서로 이들을 함께 스레드하는 방법에 대해 먼저 알아야합니다. 당신은 약속이나 더 높은 수준의 새로운 비동기 / 기다림을 도구로 사용하거나 자신의 것을 굴릴 수 있습니다.

그러나 실제 문제 영역에 익숙해지기 전에는 문제를 해결하기 위해 전술적 도구를 사용해서는 안됩니다. 이 의존성에 대한 맵을 그려야 언제 실행할 필요가 있는지 알 수 있습니다. 이러한 모든 콜백에 대한 임시 방편을 시도하는 것은 당신을 잘 대처하지 못할 것입니다.


가장 간단한 해결책은 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);    
}); 

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

예:

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

또는 (위의 것은 더 나은 익명 함수는 항상 문제입니다) :

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

더 쉬운 것은 없습니다.

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

XMLHttpRequest 고급 기능 확인

모든 최신 브라우저에서 지원됩니다. 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);

보시다시피 나는 동기화를 구현하지 않았습니다 ... 나쁜 일입니다.

그런 말을하면서 ... 쉬운 방법으로하지 않는 이유는 무엇입니까?

주석에서 언급했듯이 오류 && synchronous의 사용은 답변의 요점을 완전히 깨뜨립니다. 적절한 방법으로 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을 작성하여 모든 브라우저에서 오류가 발생하는 것입니다.

오류 처리기는 사용자 지정 헤더를 설정하고 blob 배열 버퍼 또는 기타 ...로 responseType을 설정하는 경우 유용 할 수 있습니다.

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

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

첫 번째 경우에는 this.statusText 아래의 displayAjax() 내부에서 오류가 Method not Allowed 됩니다.

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

교차 도메인 허용 안 함 오류가 자동으로 발생합니다.

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

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

오류를 완전히 제어 할 수없는 경우 오류 처리기를 추가하는 이유는 무엇입니까? 대부분의 오류는 콜백 함수 인 displayAjax() 에서이 내부로 반환됩니다.

따라서 : URL을 복사하여 붙여 넣을 수있는 경우 오류 검사가 필요 없습니다. ;)

추신 : 내가 쓴 x ( 'x', displayAjax) ... 첫 번째 테스트로서, 그리고 그것은 완전히 응답을 가지고 ... ??? 그래서 HTML이 위치한 폴더를 검사했고 'x.xml'이라는 파일이있었습니다. 따라서 파일 확장자를 잊어 버린 경우에도 XMLHttpRequest 2가 검색 됩니다. 나는 웃었다.

동기 파일 읽기

그러지 마.

잠시 동안 브라우저를 차단하려면 멋진 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 페이지에 통합하는 첫 번째 기능 중 하나는 GET과 함께이 대답의 첫 번째 Ajax 함수입니다 ...

하지만 XMLHttpRequest 2로 많은 일을 할 수있다.

나는 resume, filereader, filesystem을 가진 양면의 범위를 사용하는 다운로드 관리자, 캔버스를 사용하는 다양한 이미지 resizer 변환기, web64 데이터베이스를 base64 이미지로 채우기 등등을 만들었지 만 이러한 경우에만 함수를 작성해야한다. ... 때로는 blob, 배열 버퍼가 필요합니다. 헤더를 설정하고, mimetype을 오버라이드 할 수 있습니다.

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


자바 스크립트의 '수수께끼'에 맞서 싸우는 동안 우리가 직면하는 매우 일반적인 문제입니다. 오늘이 신비를 신비화 시키자.

간단한 JavaScript 함수로 시작해 보겠습니다.

function foo(){
// do something 
 return 'wohoo';
}

let bar = foo(); // bar is 'wohoo' here

이것은 간단한 동기 함수 호출 (각 코드 행이 다음 작업 이전에 '작업 완료'순서로 진행됨)이며 결과는 예상과 동일합니다.

이제 우리 함수에 약간의 지연을 도입하여 약간의 꼬임을 추가하여 코드의 모든 라인이 순서대로 '완료'되지 않도록하십시오. 따라서 함수의 비동기 동작을 에뮬레이션합니다.

function foo(){
 setTimeout( ()=>{
   return 'wohoo';
  }, 1000 )
}

let bar = foo() // bar is undefined here

거기에 우리가 기대했던 기능을 깨뜨린 것입니다. 그러나 정확히 무엇이 일어 났는가? 코드를 보면 꽤 논리적입니다. 이 함수 foo()는 실행시 아무 것도 반환하지 않으므로 (반환 값은 undefined) 'wohoo'를 반환하기 위해 1 초 후에 함수를 실행하는 타이머를 시작합니다. 그러나 보시다시피 막대에 지정된 값은 foo ()에서 즉시 반환되는 항목이며 나중에 나오는 항목이 아닙니다.

그렇다면이 문제를 어떻게 해결할 수 있을까요?

PROMISE에 대한 우리의 기능을 물어 봅시다 . Promise는 실제로 이것이 의미하는 바를 의미합니다. 즉,이 기능을 통해 향후에 출력물을 제공 할 수 있습니다. 위의 작은 문제에 대한 조치를 취해 보겠습니다.

function foo(){
   return new Promise( (resolve, reject) => { // I want foo() to PROMISE me something
    setTimeout ( function(){ 
      // promise is RESOLVED , when execution reaches this line of code
       resolve('wohoo')// After 1 second, RESOLVE the promise with value 'wohoo'
    }, 1000 )
  })
}

let bar ; 
foo().then( res => {
 bar = res;
 console.log(bar) // will print 'wohoo'
});

따라서, 요약은 - 아약스 기반의 호출과 같은 비동기 함수를 다루기 resolve위해 반환 할 값에 대한 약속을 사용할 수 있습니다 . 따라서, 한마디로 당신은 해결 대신 값을 반환 비동기 기능에.

UPDATE (비동기 / 대기로 약속 함)

then/catch약속을 지키는 데 사용 하는 것 외에도 한 가지 더 많은 접근 방식이 있습니다. 아이디어는 것입니다 비동기 기능을 인식 하고 약속이 기다립니다 코드의 다음 행으로 이동하기 전에, 해결하기 위해. 그것은 여전히 promises두뇌 속에서도 다른 구문 접근 방식을 사용합니다. 더 명확하게하기 위해 아래 비교를 찾을 수 있습니다.

다음 / 버전 잡기 :

function fetchUsers(){
   let users = [];
   getUsers()
   .then(_users => users = _users)
   .catch(err =>{
      throw err
   })
   return users;
 }

비동기 / 대기 버전 :

  async function fetchUsers(){
     try{
        let users = await getUsers()
        return users;
     }
     catch(err){
        throw err;
     }
  }

다음 예제는 내가 어떻게 작성했는지 보여줍니다.

  • 비동기 HTTP 호출 처리;
  • 각 API 호출에서 응답을 기다립니다.
  • 사용 Promise 패턴;
  • Promise.All 패턴을 사용하여 여러 HTTP 호출에 참여하십시오.

이 작업 예제는 독립적 인 예제입니다. 창 XMLHttpRequest개체를 사용하여 호출 하는 간단한 요청 개체를 정의 합니다. 약속이 완료되기를 기다리는 간단한 함수를 정의 할 것입니다.

문맥. 다음은 주어진 쿼리 문자열 집합에 대한 개체 를 검색하기 위해 Spotify Web API 끝점을 쿼리하는 예제입니다 playlist.

[
 "search?type=playlist&q=%22doom%20metal%22",
 "search?type=playlist&q=Adele"
]

각 항목에 대해 새로운 Promise가 블록을 실행 ExecutionBlock하고 결과를 구문 분석하고 Spotify user객체 의 목록 인 결과 배열을 기반으로 새로운 약속을 예약 하고 ExecutionProfileBlock비동기 적으로 새 HTTP 호출을 실행합니다 .

그런 다음 중첩 된 Promise 구조를 볼 수 있습니다.이 구조는 여러 비동기 중첩 HTTP 호출을 생성하고 각 호출의 하위 집합에서 결과를 결합 할 수있게합니다 Promise.all.

참고 최근 Spotify searchAPI에서는 요청 헤더에 액세스 토큰을 지정해야합니다.

-H "Authorization: Bearer {your access token}" 

따라서 요청 헤더에 액세스 토큰을 넣어야하는 다음 예제를 실행합니다.

var spotifyAccessToken = "YourSpotifyAccessToken";
var console = {
    log: function(s) {
        document.getElementById("console").innerHTML += s + "<br/>"
    }
}

// Simple XMLHttpRequest
// based on https://davidwalsh.name/xmlhttprequest
SimpleRequest = {
    call: function(what, response) {
        var request;
        if (window.XMLHttpRequest) { // Mozilla, Safari, ...
            request = new XMLHttpRequest();
        } else if (window.ActiveXObject) { // Internet Explorer
            try {
                request = new ActiveXObject('Msxml2.XMLHTTP');
            }
            catch (e) {
                try {
                  request = new ActiveXObject('Microsoft.XMLHTTP');
                } catch (e) {}
            }
        }

        // State changes
        request.onreadystatechange = function() {
            if (request.readyState === 4) { // Done
                if (request.status === 200) { // Complete
                    response(request.responseText)
                }
                else
                    response();
            }
        }
        request.open('GET', what, true);
        request.setRequestHeader("Authorization", "Bearer " + spotifyAccessToken);
        request.send(null);
    }
}

//PromiseAll
var promiseAll = function(items, block, done, fail) {
    var self = this;
    var promises = [],
                   index = 0;
    items.forEach(function(item) {
        promises.push(function(item, i) {
            return new Promise(function(resolve, reject) {
                if (block) {
                    block.apply(this, [item, index, resolve, reject]);
                }
            });
        }(item, ++index))
    });
    Promise.all(promises).then(function AcceptHandler(results) {
        if (done) done(results);
    }, function ErrorHandler(error) {
        if (fail) fail(error);
    });
}; //promiseAll

// LP: deferred execution block
var ExecutionBlock = function(item, index, resolve, reject) {
    var url = "https://api.spotify.com/v1/"
    url += item;
    console.log( url )
    SimpleRequest.call(url, function(result) {
        if (result) {

            var profileUrls = JSON.parse(result).playlists.items.map(function(item, index) {
                return item.owner.href;
            })
            resolve(profileUrls);
        }
        else {
            reject(new Error("call error"));
        }
    })
}

arr = [
    "search?type=playlist&q=%22doom%20metal%22",
    "search?type=playlist&q=Adele"
]

promiseAll(arr, function(item, index, resolve, reject) {
    console.log("Making request [" + index + "]")
    ExecutionBlock(item, index, resolve, reject);
}, function(results) { // Aggregated results

    console.log("All profiles received " + results.length);
    //console.log(JSON.stringify(results[0], null, 2));

    ///// promiseall again

    var ExecutionProfileBlock = function(item, index, resolve, reject) {
        SimpleRequest.call(item, function(result) {
            if (result) {
                var obj = JSON.parse(result);
                resolve({
                    name: obj.display_name,
                    followers: obj.followers.total,
                    url: obj.href
                });
            } //result
        })
    } //ExecutionProfileBlock

    promiseAll(results[0], function(item, index, resolve, reject) {
        //console.log("Making request [" + index + "] " + item)
        ExecutionProfileBlock(item, index, resolve, reject);
    }, function(results) { // aggregated results
        console.log("All response received " + results.length);
        console.log(JSON.stringify(results, null, 2));
    }

    , function(error) { // Error
        console.log(error);
    })

    /////

  },
  function(error) { // Error
      console.log(error);
  });
<div id="console" />

나는 광범위하게이 솔루션을 논의한 here .


이것은 몇 가지 다른 답변에 나타나는 클로저에 대한 몇 가지 (가능한) 오해를 해결하려는 시도입니다.

  • 클로저는 내부 함수를 반환 할 때 생성 될뿐만 아니라 사실, 닫는 함수를 생성하기 위해 닫는 함수 를 전혀 반환 할 필요가 없습니다 . 대신 내부 함수를 외부 범위에있는 변수에 할당하거나 나중에 즉시 호출 할 수있는 다른 함수에 인수로 전달할 수 있습니다. 따라서 내부 함수가 호출 될 때마다 내부 함수가 호출 될 때마다 내부 함수가 해당 클로저에 액세스 할 수 있기 때문에 내부 함수 가 호출되는 즉시 내부 함수가 닫히게됩니다.
  • 클로저는 해당 범위 에있는 이전 변수 의 복사본을 참조하지 않습니다 . 변수 자체는 클로저의 일부이므로 해당 변수 중 하나에 액세스 할 때 표시되는 값은 액세스 할 때의 최신 값입니다. 이것은 루프 내부에서 생성 된 내부 함수가 까다로울 수 있습니다. 함수가 생성되거나 호출 될 때 변수 복사본을 얻는 대신 각 외부 변수가 동일한 외부 변수에 액세스 할 수 있기 때문입니다.
  • 클로저의 "변수"에는 함수 내에 선언 된 명명 된 함수포함 됩니다. 또한 함수의 인수를 포함합니다. 클로저는 클로저의 변수를 전역 범위까지 액세스 할 수 있습니다.
  • 클로저는 메모리를 사용하지만 자바 스크립트는 자체적으로 참조되지 않은 순환 구조를 정리하므로 메모리 누수가 발생하지 않습니다 . 클로저를 포함하는 Internet Explorer 메모리 누수는 클로저를 참조하는 DOM 특성 값의 연결이 끊어 질 때 만들어 지므로 순환 구조에 대한 참조가 유지됩니다.






javascript ajax asynchronous ecmascript-6 ecmascript-2017