前言
本次分享的內(nèi)容是 Promise、Generator 函數(shù)與 async/await 别洪,都是與異步操作相關(guān)的內(nèi)容叨恨,我們常見的異步操作基本是:回調(diào)函數(shù)和事件監(jiān)聽,AJAX就是事件監(jiān)聽的一種挖垛,本次分享中也主要以AJAX為例痒钝。
var request = new XMLHttpRequest();
request.onreadystatechange = function () {
if (request.readyState === 4) {
if (request.status === 200) {
return success(request.responseText);
} else {
return fail(request.status);
}
}
}
request.open('GET', '/api/categories');
request.send();
Promise
首先先看一下Promise是什么:
可以看到Promise是一個構(gòu)造函數(shù),里面有我們熟悉的all痢毒、 reject 送矩、resolve 方法,原型上有 then哪替、catch 方法栋荸,我們可以新建一個Promise 對象來使用。
new Promise(
/* executor */
function(resolve, reject) {...}
);
我覺得文檔上說的比我清楚的多:
Promise對象是一個代理對象(代理一個值)凭舶。它允許你為異步操作的成功和失敗分別綁定相應(yīng)的處理方法(handlers )蒸其。 這讓異步方法可以像同步方法那樣返回值,但并不是立即返回最終執(zhí)行結(jié)果库快,而是一個能代表未來出現(xiàn)的結(jié)果的promise對象
一個 Promise有以下幾種狀態(tài):
- pending: 初始狀態(tài)摸袁,不是成功或失敗狀態(tài)。
- fulfilled: 意味著操作成功完成义屏。
- rejected: 意味著操作失敗靠汁。
pending 狀態(tài)的 Promise 對象可能觸發(fā)fulfilled 狀態(tài)并傳遞一個值給相應(yīng)的狀態(tài)處理方法,也可能觸發(fā)失敗狀態(tài)(rejected)并傳遞失敗信息闽铐。當(dāng)其中任一種情況出現(xiàn)時蝶怔,Promise 對象的 then 方法綁定的處理方法(handlers )就會被調(diào)用(then方法包含兩個參數(shù):onfulfilled 和 onrejected,它們都是 Function 類型兄墅。當(dāng)Promise狀態(tài)為fulfilled時踢星,調(diào)用 then 的 onfulfilled 方法,當(dāng)Promise狀態(tài)為rejected時隙咸,調(diào)用 then 的 onrejected 方法沐悦, 所以在異步操作的完成和綁定處理方法之間不存在競爭)成洗。
因為 Promise.prototype.then 和 Promise.prototype.catch
方法返回promise 對象, 所以它們可以被鏈?zhǔn)秸{(diào)用藏否。
疑問
既然 .then() 既有reject 也有 resolve的回調(diào)瓶殃,那為什么還有.catch()?
我看網(wǎng)上的說法是說 如果是 .then() 中報錯,則會進入 .catch() 中處理副签,這樣可以避免報錯遥椿,類似于 try catch。
基本api
- Promise.resolve()
- Promise.reject()
- Promise.prototype.then()
- Promise.prototype.catch()
- Promise.all() // 所有的完成
- Promise.race() // 競速淆储,完成一個即可
應(yīng)用
然后我們用Promise來重寫一下ajax:
function ajax (method, url, data) {
var request = new XMLHttpRequest();
return new Promise(function (resolve, reject) {
request.onreadystatechange = function () {
if (request.readyState === 4) {
if (request.status === 200) {
resolve(request.responseText);
} else {
reject(request.status);
}
}
};
request.open(method, url);
request.send(data);
});
}
var p = ajax('GET', '/api/xxxx');
p.then(function (text) {
console.log(text);
}).catch(function (status) {
console.log('ERROR: ' + status);
});
看起來好像并沒有什么改變冠场,但是如果我們想一個請求完成再發(fā)起另一個請求呢?
ajax ('GET', '/api/hhh').then(function(data) {
return ajax('POST', '/api/hhhh', data.body);
})
.then(function(data) {
return ajax('POST', '/api/hhhhh', data.body);
})
.then(function() {
console.log(data);
});
肯定比無限回調(diào)好看多了本砰。
除此之外碴裙,我們可以使用 Promise.all() 來并行執(zhí)行異步操作。
function func(num){
var p = new Promise(function(resolve, reject){
setTimeout(function(){
resolve(num);
}, 1000);
});
return p;
}
Promise.all([func(1), func(2), func(3)])
.then(function(results){
console.log(results);
});
// console的值為 [1, 2, 3]
三個異步操作的并行執(zhí)行的灌具,等到它們都執(zhí)行完后才會進到then里面,all會把所有異步操作的結(jié)果放進一個數(shù)組中傳給then譬巫,就是上面的results咖楣。
Generator 函數(shù)
之前沒怎么接觸過,是一個可以返回多次的函數(shù)芦昔。
generator 函數(shù)由 function*定義诱贿,除了 return 還可以使用 yield 返回多次。那到底怎么用咕缎?
應(yīng)用
先來個簡單的例子:
斐波那契數(shù)列珠十,一般我們要這樣寫:
function fib(max) {
var
t,
a = 0,
b = 1,
arr = [0, 1];
while (arr.length < max) {
t = a + b;
a = b;
b = t;
arr.push(t);
}
return arr;
}
fib(5); // [0, 1, 1, 2, 3]
使用 generator:
function* fib(max) {
var
t,
a = 0,
b = 1,
n = 1;
while (n < max) {
yield a;
t = a + b;
a = b;
b = t;
n ++;
}
return a;
}
var f = fib(5);
f.next(); // {value: 0, done: false}
f.next(); // {value: 1, done: false}
f.next(); // {value: 1, done: false}
f.next(); // {value: 2, done: false}
f.next(); // {value: 3, done: true}
//