我如何宣传原生XHR?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了我如何宣传原生XHR?相关的知识,希望对你有一定的参考价值。

我想在我的前端应用程序中使用(本机)promises来执行XHR请求,但没有庞大框架的所有tomfoolery。

我希望我的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请求(您可以刷新herehere

由于any browser that supports native promises也将支持xhr.onload,我们可以跳过所有onReadyStateChange tomfoolery。让我们退一步,从使用回调的基本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构造函数接受一个将传递两个参数的函数(让我们称之为resolvereject)。您可以将这些视为回调,一个用于成功,一个用于失败。示例很棒,让我们用这个构造函数更新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);
);

现在我们可以利用promises的功能,链接多个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可以找到更全面的方法。

或者,您可以使用fetch APIpolyfill)。

另一答案

这可以像下面的代码一样简单。

请记住,此代码仅在调用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函数。它有一些非常好的support

我首先使用@ SomeKittens的答案,但后来发现fetch为我开箱即用:)

另一答案

我认为我们可以通过不创建the top answer对象来使XMLHttpRequest更加灵活和可重用。这样做的唯一好处是我们不必自己编写2或3行代码,并且它具有取消对API的许多功能(如设置标头)的巨大缺点。它还从应该处理响应的代码中隐藏原始对象的属性(对于成功和错误)。因此,我们可以通过接受XMLHttpRequest对象作为输入并将其作为结果传递来制作更灵活,更广泛适用的函数。

此函数将任意XMLHttpRequest对象转换为promise,默认情况下将非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();
    );

这个函数非常自然地适合Promises链,而不会牺牲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(er

以上是关于我如何宣传原生XHR?的主要内容,如果未能解决你的问题,请参考以下文章

如何链接原生 Javascript Promise?

原生Ajax

Ajax原生XHR对象

Ajax原生XHR对象

xhr 原生兼容写法

ajax基础3--使用原生xhr发起get,post请求