itsource

네이티브 XHR을 프로미스하려면 어떻게 해야 하나요?

mycopycode 2022. 12. 24. 17:26
반응형

네이티브 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의 답변은 완벽에 가깝습니다.단, 다음과 같은 단점이 있습니다.

  1. xhr 요구가 공개되는 것은 마지막 순간뿐입니다. 하면 안 .POST요청 본문을 설정할 수 없습니다.
  2. 가 더 .send-콜은 함수 안에 숨겨져 있습니다.
  3. 실제로 요청을 할 때 꽤 많은 보일러 플레이트가 도입됩니다.

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

반응형