Notice
Recent Posts
Recent Comments
Link
«   2025/01   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Tags
more
Archives
Today
Total
관리 메뉴

후레임의 프로그래밍

비동기 호출에서 응답을 어떻게 반환합니까? 본문

스택오버플로우(Stack Overflow)

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

후레임 2020. 10. 26. 13:43
질문

 

비동기 요청을 만드는 foo함수가 있습니다. foo에서 응답 / 결과를 반환하려면 어떻게 해야합니까?

콜백에서 값을 반환하고 결과를 함수 내부의 지역 변수에 할당하고 반환하려고 시도했지만 실제로 응답을 반환하는 방법은 없습니다 (모두 undefined또는 변수 result의 초기 값).

jQuery의 ajax함수를 사용한 예 :

function foo() {
    var result;

    $.ajax({
        url: '...',
        success: function(response) {
            result = response;
            // return response; // <- I tried that one as well
        }
    });

    return result; // It always returns `undefined`
}

node.js를 사용한 예 :

function foo() {
    var result;

    fs.readFile("path/to/file", function(err, data) {
        result = data;
        // return data; // <- I tried that one as well
    });

    return result; // It always returns `undefined`
}

Promise의 then블록을 사용하는 예 :

function foo() {
    var result;

    fetch(url).then(function(response) {
        result = response;
        // return response; // <- I tried that one as well
    });

    return result; // It always returns `undefined`
}




답변

→ 다른 예를 사용한 비동기 동작에 대한보다 일반적인 설명은내 변수가 변경되지 않은 이유를 참조하세요. 함수 내부에서 수정 한 후? -비동기 코드 참조

→ 이미 문제를 이해했다면 아래 가능한 해결 방법으로 건너 뛰세요.

문제

AjaxA비동기즉, 요청 전송 (또는 응답 수신)이 정상적인 실행 흐름에서 제외됩니다. 귀하의 예에서 $. 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 the item
});
doSomethingElse();

응답을 기다리는 대신 실행이 즉시 계속되고 Ajax 호출 후 명령문이 실행됩니다. 결국 응답을 받으려면 응답이 수신되면 호출 할 함수 인 콜백을 제공합니다 (알려 두시겠습니까? 콜백?). 해당 호출 이후에 오는 모든 명령문은 콜백이 호출되기 전에 실행됩니다.


솔루션

자바 스크립트의 비동기 특성을 수용하세요!특정 비동기 작업은 동기식 작업을 제공하지만 ( 'Ajax'도 마찬가지) 일반적으로 특히 브라우저 컨텍스트에서 사용하지 않는 것이 좋습니다.

왜 나쁜가요?

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

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

다음에서 우리는 서로를 기반으로 구축되는 세 가지 솔루션을 살펴볼 것입니다.

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

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


ES2017 + : async / await를 통한 약속

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

async / await는 promise를 기반으로합니다. async함수는 항상 promise를 반환합니다. await는 프라 미스를 "언 래핑"하고 프라 미스가 해결 된 값을 생성하거나 프라 미스가 거부 된 경우 오류를 발생시킵니다.

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

async <에 대해 자세히 알아볼 수 있습니다.대기 <on 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;
  }
}

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

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


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

콜백은 함수 1이 함수 2에 전달되는 경우입니다. 함수 2는 준비가되면 함수 1을 호출 할 수 있습니다. 비동기 프로세스의 컨텍스트에서 콜백은 비동기 프로세스가 완료 될 때마다 호출됩니다. 일반적으로 결과는 콜백에 전달됩니다.

질문의 예에서 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
    });
}

콜백은 호출 할 때 foo에 전달하고 success에 전달하는 함수를 나타냅니다. 즉 Ajax 요청이 성공하면 $. ajax콜백을 호출하고 응답을 콜백에 전달합니다 (result로 참조 할 수 있음). 이것이 콜백을 정의한 방법이기 때문입니다).

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

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

It's easier to write code using callbacks than it may seem. After all, JavaScript in the browser is heavily event-driven (DOM events). Receiving the Ajax response is nothing else but an event.
Difficulties could arise when you have to work with third-party code, but most problems can be solved by just thinking through the application flow.


ES2015 + : then ()

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

약속은 미래가치를 담는 컨테이너입니다. Promise가 값을 받거나 (해결됨) 취소되면 (거부 됨)이 값에 액세스하려는 모든 "수신자"에게 알립니다. < / p>

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

다음은 약속 사용의 예입니다.

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 호출에 적용하면 다음과 같은 promise를 사용할 수 있습니다.

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

Promise가 제공하는 모든 이점을 설명하는 것은이 답변의 범위를 벗어납니다.하지만 새 코드를 작성하는 경우이를 심각하게 고려해야합니다. 그들은 코드의 훌륭한 추상화와 분리를 제공합니다.

Promise에 대한 추가 정보 : HTML5 락-자바 스크립트 약속

참고 : jQuery의 지연된 객체

지연된 객체는 jQuery의 약속의 사용자 지정 구현입니다 (Promise API가 표준화되기 전). 거의 약속처럼 동작하지만 약간 다른 API를 노출합니다.

jQuery의 모든 Ajax 메소드는 함수에서 반환 할 수있는 "지연된 객체"(실제로는 지연된 객체의 약속)를 이미 반환합니다.

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

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

참고 : 약속 문제

Promise와 Deferred Object는 미래 가치에 대한 컨테이너일뿐 가치 자체가 아닙니다. 예를 들어 다음이 있다고 가정합니다.

function checkPassword() {
    return $.ajax({
        url: '/password',
        data: {
            username: $('#username').val(),
            password: $('#password').val()
        },
        type: 'POST',
        dataType: 'json'
    });
}

if (checkPassword()) {
    // Tell the user they're logged in
}

이 코드는 위의 비 동시성 문제를 오해합니다. 특히 $. ajax ()는 서버의 '/ password'페이지를 확인하는 동안 코드를 고정하지 않습니다. 서버에 요청을 보내고 기다리는 동안 즉시 jQuery를 반환합니다. 서버의 응답이 아닌 Ajax Deferred 객체. 즉, if문은 항상이 Deferred 개체를 가져 와서 true로 취급하고 사용자가 로그인 한 것처럼 진행합니다. 좋지 않습니다.

하지만 수정은 쉽습니다.

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

권장되지 않음 : 동기식 "Ajax"호출

앞서 언급했듯이 일부 (!) 비동기 작업에는 동기 작업이 있습니다. 나는 그들의 사용을 옹호하지는 않지만 완전성을 위해 동기 호출을 수행하는 방법은 다음과 같습니다.

jQuery없이

XMLHttpRequest객체를 직접 사용하는 경우 를 전달합니다. false.open에 대한 세 번째 인수로 사용.

jQuery

jQuery를 사용하는 경우 비동기를 설정할 수 있습니다. 옵션을 false로 설정합니다. 이 옵션은 jQuery 1.8부터 사용되지 않습니다. 그런 다음 여전히 success콜백을 사용하거나 jqXHR 객체:

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

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

주의!동기식 JSONP를 만들 수 없습니다. 의뢰. 본질적으로 JSONP는 항상 비동기 적입니다 (이 옵션을 고려하지 않는 또 하나의 이유).




출처 : https://stackoverflow.com/questions/14220321