javascript - 콜백함수 - 자바스크립트 함수 실행 순서




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

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

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

문제

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는 본질적으로 항상 비동기 적입니다 (이 옵션을 고려조차하지 않는 또 하나의 이유).

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

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


코드에서 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읽으십시오 .


2017 답 : 이제 모든 현재 브라우저와 노드에서 원하는 것을 정확하게 할 수 있습니다.

이것은 매우 간단합니다.

  • 약속을 되 찾으십시오.
  • await 사용하십시오. 그러면 JavaScript가 HTTP 응답과 같은 값으로 해결 될 것이라는 약속을 기다립니다.
  • async/await 키워드를 상위 함수에 추가하십시오.

다음은 코드의 작동 버전입니다.

(async function(){

var response = await superagent.get('...')
console.log(response)

})()

모든 현재 브라우저 및 노드 8에서 지원됩니다.


다음은 비동기 요청에 대한 몇 가지 접근 방식입니다.

  1. then()
  2. Q - JavaScript 용 약속 라이브러리
  3. + Promises.js
  4. jQuery 지연
  5. XMLHttpRequest API
  6. 콜백 개념 사용 - 첫 번째 응답 구현

예 : 여러 요청을 처리하기위한 jQuery 지연 구현

var App = App || {};

App = {
    getDataFromServer: function(){

      var self = this,
                 deferred = $.Deferred(),
                 requests = [];

      requests.push($.getJSON('request/ajax/url/1'));
      requests.push($.getJSON('request/ajax/url/2'));

      $.when.apply(jQuery, requests).done(function(xhrResponse) {
        return deferred.resolve(xhrResponse.result);
      });
      return deferred;
    },

    init: function(){

        this.getDataFromServer().done(_.bind(function(resp1, resp2) {

           // Do the operations which you wanted to do when you
           // get a response from Ajax, for example, log response.
        }, this));
    }
};
App.init();


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 응답을 반환하는 방법입니다. (쉬운 방법을 추가했습니다.)


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

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/


짧은 대답 : 함수가 반환 된 후 호출이 비동기 적으로 실행 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.


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)

})

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

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

다음은 같은 예입니다.

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비동기 작업 중에 값을 저장하는 객체. 따라서 비동기 작업 후에도 결과를 사용할 수 있습니다.

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


약속과 콜백은 여러 상황에서 잘 작동하지만 뒤의 고통은 다음과 같이 표현됩니다.

if (!name) {
  name = async1();
}
async2(name);

결국 끝날거야 async1. if name가 정의되지 않았 는지 확인 하고 그에 따라 콜백을 호출하십시오.

async1(name, callback) {
  if (name)
    callback(name)
  else {
    doSomething(callback)
  }
}

async1(name, async2)

그 동안 괜찮 작은 예에 당신이 참여 유사한 사례와 오류 처리를 많이 가지고 때 짜증나 가져옵니다.

Fibers 문제를 해결하는 데 도움이됩니다.

var Fiber = require('fibers')

function async1(container) {
  var current = Fiber.current
  var result
  doSomething(function(name) {
    result = name
    fiber.run()
  })
  Fiber.yield()
  return result
}

Fiber(function() {
  var name
  if (!name) {
    name = async1()
  }
  async2(name)
  // Make any number of async calls from here
}

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 함수 내에서 직접 원하는 것을 수행해야합니다.


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

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

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


성공 의 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();

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

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

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

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

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

간단한 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;
     }
  }

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

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

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




ecmascript-2017