我假设您知道如何发出本机XHR请求
由于任何支持本机Promise的浏览器也将支持
xhr.onload,因此我们可以跳过所有的
onReadyStateChangetomfoolery。让我们退后一步,从使用回调的基本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数据),但足以使我们前进。
Promise构造函数
我们可以这样构造一个承诺:
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);});现在,我们可以利用promise的功能,将多个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参数和自定义标头。让我们使用带有签名的options对象而不是多个参数:
{ 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 enpreURIComponent(key) + '=' + enpreURIComponent(params[key]); }).join('&'); } xhr.send(params); });}// Headers and params are optionalmakeRequest({ 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);});。



