本文通過實踐從古至今對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+