本文通过实践从古至今对XMLHttpRequest
封装的各种方式,来了解在es中异步编程的实现细节和设计模式。
回顾XMLHttpRequest
原生写法
var xhr = new XMLHttpRequest()
xhr.timeout = 2000
xhr.open("POST", "path/to/api")
xhr.onload= function () {console.log(xhr.status,xhr.responseText)}
xhr.setRequestHeader("Content-type","text/plain;charset=UTF-8")
xhr.send('{"key":"value"}')
常见封装方法
1.回调函数
传值方式实现
function request(method, url, done) {
var xhr = new XMLHttpRequest()
xhr.open(method, url)
xhr.onload = function () {
if (this.status >= 200 && this.status < 300)
done(null, xhr.response)
else
done(xhr.response)
}
xhr.onerror = function () {done(xhr.response)}
xhr.send()
}
//---
request('GET', 'path/to/api', function (err, res) {
if (err){console.error(err)}
console.log(res)
})
传对象方式实现
function request(obj){
var xhr = new XMLHttpRequest()
xhr.open(obj.method, obj.url)
xhr.onload= function () {
if (this.status >= 200 && this.status < 300)
obj.success(xhr.response)
else
obj.fail(xhr.response)
}
xhr.onerror= function () {obj.fail(xhr.response)}
for(let key in obj.header){xhr.setRequestHeader(key ,obj.header[key])}
xhr.send(obj.data)
}
//---
request({
url: 'path/to/api',
data: {},
method:"GET",
header: {'Content-type': 'application/json'},
success(res) {console.log(res)},
fail(err) {console.error(err)}
})
2.Promise
使用 Promise – MDN
Promise 本质上是一个绑定了回调的对象,而不是将回调传进函数内部。
传值方式实现
function requestPromise(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()
})
}
// ---
requestPromise('GET', 'path/to/api')
.then(function (res1) {
return makeRequest('GET', res1.url)
})
.then(function (res2) {
console.log(res2)
})
.catch(function (err) {
console.error(err)
});
传对象方式实现
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);
});
3.fetch ,Request (基于promise)
fetch('path/to/api', {credentials: 'include'})
.then(function(response) {
if (response.status >= 400) {
throw new Error("Bad response from server");
}
return response.json();
})
.then(function(stories) {
console.log(stories);
});
var myRequest = new Request(
'path/to/api',
{ method: 'GET',
headers: {'Content-Type': 'application/json'},
mode: 'cors',
cache: 'default'
}
)
fetch(myRequest).then(function(response) {
//...
});
4.async/await (ES7)
async function f1() {
var res = await requestPromise('GET', 'path/to/api');
console.log(res)
}
f1();
其他
sleep 实现 (async/await)
function sleep (time) {
return new Promise(function (resolve, reject) {setTimeout(resolve, time)})
}
var start = async function () {
for (var i = 1; i <= 10; i++) {
console.log(`当前是第${i}次等待..`);
await sleep(1000);
}
};
start();
co+Generator
感觉是 async/await 到来之前的过渡方案
const co=require('co')
//let begin = new Date();
co(function* (){
let buyed = yield buySomething();
//console.log(buyed ,new Date() - begin);
let cleaned = yield clean();
//console.log(cleaned ,new Date() - begin);
let cook_and_wash = yield [cook(),wash()];
//console.log(cook_and_wash ,new Date() - begin);
let eated = yield eat();
//console.log(eated,new Date() - begin);
});
异步流程控制库
参考
XMLHttpRequest -MDN
使用 Promise – MDN
How do I promisify native XHR? – starkoverflow
深入理解ES7的async/await
fetch-MDN
Request-MDN
async function -MDN
await -MDN
RxJS 中文文档
Promises/A+