후레임의 프로그래밍
비동기 호출에서 응답을 어떻게 반환합니까? 본문
질문
비동기 요청을 만드는 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`
}
답변
→ 다른 예를 사용한 비동기 동작에 대한보다 일반적인 설명은내 변수가 변경되지 않은 이유를 참조하세요. 함수 내부에서 수정 한 후? -비동기 코드 참조
→ 이미 문제를 이해했다면 아래 가능한 해결 방법으로 건너 뛰세요.
문제
Ajax의 A는 비동기즉, 요청 전송 (또는 응답 수신)이 정상적인 실행 흐름에서 제외됩니다. 귀하의 예에서 $. 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 버전은 비동기 함수에 대한 구문 수준 지원을 도입했습니다. async
및 await
의 도움으로 "동기식 스타일"로 비동기식으로 작성할 수 있습니다. 코드는 여전히 비동기 적이지만 읽기 / 이해하기가 더 쉽습니다.
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
'스택오버플로우(Stack Overflow)' 카테고리의 다른 글
Linux에서 특정 텍스트가 포함 된 모든 파일을 찾으려면 어떻게합니까? (0) | 2020.10.26 |
---|---|
JavaScript 비교에서 ==와 ===의 차이는 무엇일까요? (0) | 2020.10.26 |
추적되었지만 현재 .gitignore에 있는 파일을 Git이 "잊게"만드는 방법은 무엇입니까? (0) | 2020.10.26 |
파일이 예외없이 존재하는지 어떻게 확인합니까? (0) | 2020.10.26 |
파이썬의 메타 클래스는 무엇입니까? (0) | 2020.10.26 |