네이티브 XHR을 프로미스하려면 어떻게 해야 하나요?
프런트 엔드 앱에서 (원어민) 약속을 사용하여 XHR 요청을 수행하고 싶지만 대규모 프레임워크의 모든 속임수는 사용하지 않고 싶습니다.
xhr이 약속을 반환하고 싶지만 이 작업이 작동하지 않습니다(다음 정보 제공:Uncaught TypeError: Promise resolver undefined is not a function
)
function makeXHRRequest (method, url, done) {
var xhr = new XMLHttpRequest();
xhr.open(method, url);
xhr.onload = function() { return new Promise().resolve(); };
xhr.onerror = function() { return new Promise().reject(); };
xhr.send();
}
makeXHRRequest('GET', 'http://example.com')
.then(function (datums) {
console.log(datums);
});
네이티브 XHR 요청 방법을 알고 있을 것입니다(여기저기서 복습할 수 있습니다).
네이티브 약속을 지원하는 모든 브라우저가xhr.onload
할 수 onReadyStateChange
한 콜백을 부터 시작합시다.콜백은 XHR 요구 함수입니다.
function makeRequest (method, url, done) {
var xhr = new XMLHttpRequest();
xhr.open(method, url);
xhr.onload = function () {
done(null, xhr.response);
};
xhr.onerror = function () {
done(xhr.response);
};
xhr.send();
}
// And we'd call it as such:
makeRequest('GET', 'http://example.com', function (err, datums) {
if (err) { throw err; }
console.log(datums);
});
만세! 이것은 매우 복잡한 것(커스텀 헤더나 POST 데이터 등)을 수반하지 않지만, 우리가 앞으로 나아갈 수 있도록 하기에는 충분합니다.
약속 생성자
다음과 같은 약속을 구성할 수 있습니다.
new Promise(function (resolve, reject) {
// Do some Async stuff
// call resolve if it succeeded
// reject if it failed
});
의 인수를 합니다(이러한 함수를 「인수」라고 resolve
★★★★★★★★★★★★★★★★★」reject
콜백이라고 생각할 수 있습니다.하나는 성공용이고 다른 하나는 실패용입니다.멋져요, 업데이트합시다.makeRequest
"Discountor"를 사용합니다.
function makeRequest (method, url) {
return new Promise(function (resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open(method, url);
xhr.onload = function () {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(xhr.response);
} else {
reject({
status: xhr.status,
statusText: xhr.statusText
});
}
};
xhr.onerror = function () {
reject({
status: xhr.status,
statusText: xhr.statusText
});
};
xhr.send();
});
}
// Example:
makeRequest('GET', 'http://example.com')
.then(function (datums) {
console.log(datums);
})
.catch(function (err) {
console.error('Augh, there was an error!', err.statusText);
});
약속의 하여 여러 콜(및 XHR 콜)을 할 수 ..catch
는, 에 트리거 됩니다). : , 、 「 」 、 「 」 、 「 」
makeRequest('GET', 'http://example.com')
.then(function (datums) {
return makeRequest('GET', datums.url);
})
.then(function (moreDatums) {
console.log(moreDatums);
})
.catch(function (err) {
console.error('Augh, there was an error!', err.statusText);
});
POST/PUT 파라미터와 커스텀헤더를 모두 추가함으로써 이를 더욱 개선할 수 있습니다.여러 인수 대신 다음 서명이 있는 옵션개체를 사용합니다.
{
method: String,
url: String,
params: String | Object,
headers: Object
}
makeRequest
뭇매를 맞다
function makeRequest (opts) {
return new Promise(function (resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open(opts.method, opts.url);
xhr.onload = function () {
if (xhr.status >= 200 && xhr.status < 300) {
resolve(xhr.response);
} else {
reject({
status: xhr.status,
statusText: xhr.statusText
});
}
};
xhr.onerror = function () {
reject({
status: xhr.status,
statusText: xhr.statusText
});
};
if (opts.headers) {
Object.keys(opts.headers).forEach(function (key) {
xhr.setRequestHeader(key, opts.headers[key]);
});
}
var params = opts.params;
// We'll need to stringify if we've been given an object
// If we have a string, this is skipped.
if (params && typeof params === 'object') {
params = Object.keys(params).map(function (key) {
return encodeURIComponent(key) + '=' + encodeURIComponent(params[key]);
}).join('&');
}
xhr.send(params);
});
}
// Headers and params are optional
makeRequest({
method: 'GET',
url: 'http://example.com'
})
.then(function (datums) {
return makeRequest({
method: 'POST',
url: datums.url,
params: {
score: 9001
},
headers: {
'X-Subliminal-Message': 'Upvote-this-answer'
}
});
})
.catch(function (err) {
console.error('Augh, there was an error!', err.statusText);
});
보다 포괄적인 접근방식은 MDN에서 확인할 수 있습니다.
또는 fetch API(poly fill)를 사용할 수도 있습니다.
이것은 다음 코드처럼 단순할 수 있습니다.
는, 「 」 「 」 「 」 「 」 에서만 하는 것에 주의해 .reject
(콜백) 시onerror
는(네트워크 오류만) 호출되며 HTTP 상태 코드가 오류를 나타내는 경우는 호출되지 않습니다.다른 모든 예외도 제외됩니다.그런 건 IMO에게 맡겨야 해요.
외에 '라고 .reject
「」)Error
사건 자체가 아니라 단순함을 위해서 그냥 떠났어요.
function request(method, url) {
return new Promise(function (resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open(method, url);
xhr.onload = resolve;
xhr.onerror = reject;
xhr.send();
});
}
그리고 그것을 호출하는 것은 다음과 같습니다.
request('GET', 'http://google.com')
.then(function (e) {
console.log(e.target.response);
}, function (e) {
// handle errors
});
지금 검색하는 사용자는 가져오기 기능을 사용할 수 있습니다.꽤 잘 받쳐주고 있어요.
fetch('http://example.com/movies.json')
.then(response => response.json())
.then(data => console.log(data));
에 @ 그 에 @SomeKittens의 답을 발견했습니다.fetch
할 수 :) ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★」
상위 답변을 작성하지 않고 보다 유연하고 재사용할 수 있습니다.XMLHttpRequest
그렇게 함으로써 얻을 수 있는 유일한 장점은 우리가 직접 코드를 두세 줄 쓸 필요가 없다는 것입니다.또한 헤더를 설정하는 등 API의 많은 기능에 대한 접근을 빼앗는다는 큰 단점이 있습니다.또한 (성공과 오류 모두에 대해) 응답을 처리할 코드로부터 원래 개체의 속성을 숨깁니다. 이 기능을 더더 적용할 수 있는 수 .XMLHttpRequest
오브젝트를 입력으로 하여 그 결과로 전달합니다.
의 것을 합니다.XMLHttpRequest
기본적으로는 200 이외의 상태 코드를 오류로 처리한다.
function promiseResponse(xhr, failNon2xx = true) {
return new Promise(function (resolve, reject) {
// Note that when we call reject, we pass an object
// with the request as a property. This makes it easy for
// catch blocks to distinguish errors arising here
// from errors arising elsewhere. Suggestions on a
// cleaner way to allow that are welcome.
xhr.onload = function () {
if (failNon2xx && (xhr.status < 200 || xhr.status >= 300)) {
reject({request: xhr});
} else {
resolve(xhr);
}
};
xhr.onerror = function () {
reject({request: xhr});
};
xhr.send();
});
}
는 매우 .Promise
「」의 유연성을 희생하지 XMLHttpRequest
API:
Promise.resolve()
.then(function() {
// We make this a separate function to avoid
// polluting the calling scope.
var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://stackoverflow.com/');
return xhr;
})
.then(promiseResponse)
.then(function(request) {
console.log('Success');
console.log(request.status + ' ' + request.statusText);
});
catch
샘플 코드를 단순하게 유지하기 위해 위에서 생략했습니다.항상 가지고 있어야 합니다.론음
Promise.resolve()
.then(function() {
var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://stackoverflow.com/doesnotexist');
return xhr;
})
.then(promiseResponse)
.catch(function(err) {
console.log('Error');
if (err.hasOwnProperty('request')) {
console.error(err.request.status + ' ' + err.request.statusText);
}
else {
console.error(err);
}
});
또한 HTTP 상태 코드 처리를 비활성화해도 코드를 크게 변경할 필요가 없습니다.
Promise.resolve()
.then(function() {
var xhr = new XMLHttpRequest();
xhr.open('GET', 'https://stackoverflow.com/doesnotexist');
return xhr;
})
.then(function(xhr) { return promiseResponse(xhr, false); })
.then(function(request) {
console.log('Done');
console.log(request.status + ' ' + request.statusText);
});
우리의 통화 코드는 더 길지만, 개념적으로 무슨 일이 일어나고 있는지 이해하는 것은 여전히 간단하다.또한 웹 요청 API 전체를 재구축하여 기능을 지원할 필요가 없습니다.
코드를 정리하기 위해 몇 가지 편리한 기능을 추가할 수도 있습니다.
function makeSimpleGet(url) {
var xhr = new XMLHttpRequest();
xhr.open('GET', url);
return xhr;
}
function promiseResponseAnyCode(xhr) {
return promiseResponse(xhr, false);
}
그 후, 우리의 코드는 다음과 같습니다.
Promise.resolve(makeSimpleGet('https://stackoverflow.com/doesnotexist'))
.then(promiseResponseAnyCode)
.then(function(request) {
console.log('Done');
console.log(request.status + ' ' + request.statusText);
});
제 생각에 jpmc26의 답변은 완벽에 가깝습니다.단, 다음과 같은 단점이 있습니다.
- xhr 요구가 공개되는 것은 마지막 순간뿐입니다. 하면 안 .
POST
요청 본문을 설정할 수 없습니다. - 가 더 .
send
-콜은 함수 안에 숨겨져 있습니다. - 실제로 요청을 할 때 꽤 많은 보일러 플레이트가 도입됩니다.
xhr 객체에 패치를 적용하면 다음 문제가 해결됩니다.
function promisify(xhr, failNon2xx=true) {
const oldSend = xhr.send;
xhr.send = function() {
const xhrArguments = arguments;
return new Promise(function (resolve, reject) {
// Note that when we call reject, we pass an object
// with the request as a property. This makes it easy for
// catch blocks to distinguish errors arising here
// from errors arising elsewhere. Suggestions on a
// cleaner way to allow that are welcome.
xhr.onload = function () {
if (failNon2xx && (xhr.status < 200 || xhr.status >= 300)) {
reject({request: xhr});
} else {
resolve(xhr);
}
};
xhr.onerror = function () {
reject({request: xhr});
};
oldSend.apply(xhr, xhrArguments);
});
}
}
사용법은 다음과 같이 간단합니다.
let xhr = new XMLHttpRequest()
promisify(xhr);
xhr.open('POST', 'url')
xhr.setRequestHeader('Some-Header', 'Some-Value')
xhr.send(resource).
then(() => alert('All done.'),
() => alert('An error occured.'));
물론 이로 인해 다음과 같은 다른 단점이 발생합니다.원숭이를 잡는 것은 퍼포먼스를 해친다.단, 사용자가 주로 xhr의 결과를 기다리고 있으며 요청 자체가 콜 셋업보다 훨씬 오래 걸리고 xhr 요구가 자주 전송되지 않는다고 가정하는 경우에는 문제가 되지 않습니다.
PS: 물론 최신 브라우저를 타깃으로 하고 있다면 fetch를 사용하세요!
PPS: 이 방법은 표준 API를 변경하기 때문에 혼란스러울 수 있다는 지적이 댓글에서 제기되었습니다.하게 하기 을 xhr 오브젝트에 할 수 .sendAndGetPromise()
.
오래된 브라우저에서 코드를 사용하려면 HTML 문서의 <head>에 다음 내용을 입력합니다.
<script>
self.Promise||document.write("<script src=/path/to/promise/polyfill.js><\/script>");
</script>
/path/to/promise/polyfill.js를 Promise polyfill 경로로 바꿉니다.클래스가 네이티브가 아닌 경우 Promise 클래스가 생성되며 Internet Explorer 등의 오래된 브라우저에서 코드가 실행될 수 있습니다.Internet Explorer와 다른 오래된 브라우저들은 시장 점유율에서 아주 적은 부분을 차지하고 있는데, 이것은 여전히 수백만 명의 사용자들로 해석되기 때문에 나는 이 사용자들을 완전히 무시하는 것을 추천하지 않는다.
이 Promise polyfill을 제안해도 될까요?
https://github.com/stefanpenner/es6-promise/
이제 Promise 클래스에 액세스할 수 있습니다.
IE 6-8과 같은 매우 오래된 브라우저에서 코드를 사용하려면 온로드 대신 onready state change를 사용해야 합니다.현재 모든 브라우저에서 하위 호환성을 위해 onready state change가 계속 사용되므로 이 경우에도 문제가 없습니다.
function send_request(xhr, data, timeout) {
return new Promise(function (resolve, reject) {
var s, p, i;
if (data && data.constructor==Object) {// serialize object
s = "_="+(new Date).getTime();
for (p in data) if (data.hasOwnProperty(p)) {
if (!data[p] || data[p].constructor!=Array) {
data[p] = [data[p]]
}
for (i=0; i<data[p].length; i++) {
s+= "&"+encodeuricomponent(p)+"="+encodeuricomponent(data[p][i]);
}
}
data = s;
}
xhr.onreadystatechange = function() {
if (xhr.readyState==4) {
resolve(xhr);
}
}
xhr.send(data);
if (timeout) {
settimeout(function() {
reject("timeout");
xhr.abort();
}, timeout);// milliseconds until timeout
}
});
}
xhr = new XMLHttpRequest();
xhr.open("GET", "/some/file", true);
send_request(xhr).then(function(xhr) {
if (xhr.status>=200 || xhr.status<400) {
//success
alert(xhr.responseText);
}
else {
return Promise.reject(xhr.statusText? xhr.status+" "+xhr.statusText: "error");
}
})
IE 6은 XMLHttpRequest를 지원하지 않으므로 ActiveX에서 할 수 있는 폴리필도 필요합니다.문서 <head>에 기재되어 있는 다음과 같은 것이 동작할 수 있습니다.
<!--[if lt IE 7]>
<script>
// This is just an example. Use at your own risk.
function XMLHttpRequest() {
try {
return new ActiveXObject("Msxml2.XMLHTTP.6.0")
}
catch (e) {
return new ActiveXObject("Msxml2.XMLHTTP.3.0")
}
}
</script>
<![endif]-->
언급URL : https://stackoverflow.com/questions/30008114/how-do-i-promisify-native-xhr
'itsource' 카테고리의 다른 글
2개의 인덱스의 "OR"을 더 빠른 솔루션으로 대체(UNION)? (0) | 2022.12.24 |
---|---|
Log4J2 2.11.0 -> Log4J2 2.17.1 업그레이드 - Percona 5.7로의 mariaDB JDBC 드라이버에 대해 appender가 파손됨 - 잘못된 형식의 패킷 - maven - 해결됨 (0) | 2022.12.24 |
오류 "Microsoft Visual C++ 14.0이 필요합니다(vcvarsall.bat을 찾을 수 없습니다)" (0) | 2022.12.24 |
React JS -JS와 .JSX의 비교 (0) | 2022.12.24 |
Mysql 1050 오류 "테이블은 이미 존재합니다" 실제로는 존재하지 않습니다. (0) | 2022.12.24 |