我如何promisative本机XHR?
我想在我的前端应用程序中使用(本机)promise来执行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
。 让我们退后一步,使用callback函数开始一个基本的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数据),但足以让我们前进。
承诺构造函数
我们可以这样构build一个承诺:
new Promise(function (resolve, reject) { // Do some Async stuff // call resolve if it succeeded // reject if it failed });
承诺构造函数接受一个函数,将传递两个参数(让我们称它们resolve
和reject
)。 你可以把这些看作是callback,一个是成功,一个是失败。 例子很棒,让我们用这个构造函数更新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可以find更全面的方法。
或者,您可以使用获取API ( polyfill )。
这可以像下面的代码一样简单。
请记住,此代码只会在调用onerror
(仅限networking错误)时触发reject
callback,而不会在HTTP状态代码表示错误时触发reject
callback。 这也将排除所有其他例外。 处理这些应该由你决定,国际海事组织。
此外,build议使用Error
实例调用reject
callback,而不是事件本身,但为了简单起见,我保持原样。
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 });
对于任何人现在search这个,你可以使用获取function。 它有一些相当不错的支持 。
我已经使用@ SomeKittens的答案,但后来发现fetch
这对我来说是开箱即用:)