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
});
promise 생성자는 두 개의 인수를 전달하는 함수를 사용합니다 ( resolve
및 호출 reject
). 이를 콜백, 성공 및 실패에 대한 콜백이라고 생각할 수 있습니다. 예제는 훌륭합니다. makeRequest
이 생성자로 업데이트하겠습니다 :
function makeRequest (method, url) {
return new Promise(function (resolve, reject) {
var xhr = new XMLHttpRequest();
xhr.open(method, url);
xhr.onload = function () {
if (this.status >= 200 && this.status < 300) {
resolve(xhr.response);
} else {
reject({
status: this.status,
statusText: xhr.statusText
});
}
};
xhr.onerror = function () {
reject({
status: this.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 (this.status >= 200 && this.status < 300) {
resolve(xhr.response);
} else {
reject({
status: this.status,
statusText: xhr.statusText
});
}
};
xhr.onerror = function () {
reject({
status: this.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 에서 찾을 수 있습니다 .
답변
이것은 다음 코드처럼 간단 할 수 있습니다.
이 코드는 HTTP 상태 코드가 오류를 나타내는 경우가 아니라 호출 된 경우 ( 네트워크 오류 만) reject
콜백을 발생시킵니다. 다른 모든 예외도 제외됩니다. 그 취급은 당신에게 달려 있습니다, IMO.onerror
또한 이벤트 자체가 아닌 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
합니다. 그렇게하는 것의 유일한 이점은 우리가 직접 2 ~ 3 줄의 코드를 작성할 필요가 없으며 헤더 설정과 같은 많은 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
-requests가 요청 본문을 설정 하도록 허용하지 않습니다 . - 중요한
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의 결과를 기다리고 있다고 가정하고, 요청 자체가 호출 설정보다 x 더 긴 시간이 걸리고 xhr 요청이 자주 전송되지 않는다고 가정하면 문제가되지 않습니다.
추신 : 물론 최신 브라우저를 대상으로하는 경우 가져 오기를 사용하십시오!
PPS :이 방법은 혼란 스러울 수있는 표준 API를 변경한다는 의견에서 지적되었습니다. 명확성을 높이기 위해 xhr 객체에 다른 메소드를 패치 할 수 sendAndGetPromise()
있습니다.