Javascript 我如何承诺原生XHR?

Javascript 我如何承诺原生XHR?,javascript,xmlhttprequest,promise,Javascript,Xmlhttprequest,Promise,我想在我的前端应用程序中使用(本机)承诺来执行XHR请求,但不需要大规模框架的愚蠢 我希望我的xhr返回一个承诺,但这不起作用(给我:uncaughttypeerror:promise resolver undefined不是一个函数) 函数makexhrequest(方法、url、完成){ var xhr=new XMLHttpRequest(); open(方法,url); xhr.onload=function(){返回新承诺().resolve();}; xhr.onerror=func

我想在我的前端应用程序中使用(本机)承诺来执行XHR请求,但不需要大规模框架的愚蠢

我希望我的xhr返回一个承诺,但这不起作用(给我:
uncaughttypeerror:promise resolver undefined不是一个函数

函数makexhrequest(方法、url、完成){
var xhr=new XMLHttpRequest();
open(方法,url);
xhr.onload=function(){返回新承诺().resolve();};
xhr.onerror=function(){return new Promise().reject();};
xhr.send();
}
makexhrequest('GET','http://example.com')
.然后(功能(基准){
控制台日志(基准);
});

我假设您知道如何发出本机XHR请求(您可以重新整理并使用)

因为它还支持
xhr.onload
,所以我们可以跳过所有的
onReadyStateChange
tom傻瓜。让我们后退一步,用回调开始一个基本的XHR请求函数:

函数makeRequest(方法、url、完成){
var xhr=new XMLHttpRequest();
open(方法,url);
xhr.onload=函数(){
完成(空,xhr.响应);
};
xhr.onerror=函数(){
完成(xhr.响应);
};
xhr.send();
}
//我们这样称呼它:
makeRequest('GET','http://example.com,函数(错误,基准){
if(err){throw err;}
控制台日志(基准);
});
万岁!这并不涉及任何非常复杂的事情(如自定义标题或帖子数据),但足以让我们向前迈进

承诺构造函数 我们可以这样构建一个承诺:

新承诺(功能(解析、拒绝){
//做一些异步的事情
//如果成功,请调用resolve
//如果失败则拒绝
});
promise构造函数接受一个函数,该函数将被传递两个参数(让我们调用它们
resolve
reject
)。你可以把这些看作是回调,一个是成功,一个是失败。示例非常棒,让我们用这个构造函数更新
makeRequest

函数makeRequest(方法、url){
返回新承诺(功能(解决、拒绝){
var xhr=new XMLHttpRequest();
open(方法,url);
xhr.onload=函数(){
如果(this.status>=200&&this.status<300){
解析(xhr.response);
}否则{
拒绝({
状态:这个状态,
statusText:xhr.statusText
});
}
};
xhr.onerror=函数(){
拒绝({
状态:这个状态,
statusText:xhr.statusText
});
};
xhr.send();
});
}
//例如:
makeRequest('GET','http://example.com')
.然后(功能(基准){
控制台日志(基准);
})
.catch(函数(err){
console.error('Augh,有一个错误!',err.statusText);
});
现在,我们可以利用承诺的力量,链接多个XHR调用(并且,
.catch
将触发任一调用的错误):

makeRequest('GET','http://example.com')
.然后(功能(基准){
返回makeRequest('GET',datums.url);
})
.然后(功能(更多数据){
控制台日志(更多数据);
})
.catch(函数(err){
console.error('Augh,有一个错误!',err.statusText);
});
我们可以进一步改进这一点,添加POST/PUT参数和自定义头。让我们使用一个选项对象而不是多个参数,签名为:

{
方法:字符串,
url:String,
参数:字符串|对象,
标题:对象
}
makeRequest
现在看起来像这样:

函数生成请求(opts){
返回新承诺(功能(解决、拒绝){
var xhr=new XMLHttpRequest();
open(opts.method,opts.url);
xhr.onload=函数(){
如果(this.status>=200&&this.status<300){
解析(xhr.response);
}否则{
拒绝({
状态:这个状态,
statusText:xhr.statusText
});
}
};
xhr.onerror=函数(){
拒绝({
状态:这个状态,
statusText:xhr.statusText
});
};
if(选择标题){
Object.key(opts.headers).forEach(function(key){
setRequestHeader(key,opts.headers[key]);
});
}
var params=opts.params;
//如果给了我们一个对象,我们就需要进行字符串化
//如果我们有一个字符串,这将被跳过。
if(params&&typeof params==='object'){
params=Object.keys(params).map(函数(键){
返回encodeURIComponent(键)+'='+encodeURIComponent(参数[key]);
}).加入(“&”);
}
xhr.send(参数);
});
}
//标题和参数是可选的
提出请求({
方法:“GET”,
网址:'http://example.com'
})
.然后(功能(基准){
返回makeRequest({
方法:“POST”,
url:datums.url,
参数:{
分数:9001
},
标题:{
“X-潜意识信息”:“投票表决这个答案”
}
});
})
.catch(函数(err){
console.error('Augh,有一个错误!',err.statusText);
});
可在以下网址找到更全面的方法


或者,您可以使用()。

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

请记住,此代码仅在调用
onerror
时(networkerrors only)才会触发
reject
回调,而不会在HTTP状态代码表示错误时触发。这也将排除所有其他例外情况。处理这些问题应该由你来决定,国际海事组织

此外,建议使用
Error
实例调用
reject
回调,而不要调用事件本身,但为了简单起见,我保留了原样

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
    });

对于现在搜索此项的任何人,都可以使用此函数。 它有一些相当好的

我首先使用了@SomeKittens的答案,但后来发现
fetch
为我提供了开箱即用的解决方案:)

我认为我们可以通过不让它创建
XMLHttpRequest来实现更灵活和可重用性
fetch('http://example.com/movies.json')
  .then(response => response.json())
  .then(data => console.log(data));
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.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);
});
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);
    }
});
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);
});
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);
});
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.'));
<script>
self.Promise||document.write("<script src=/path/to/promise/polyfill.js><\/script>");
</script>
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");
    }
})
<!--[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]-->