Generator
Generator 函數(shù)是 ES6 提供的一種異步編程解決方案。Generator函數(shù)在function關(guān)鍵字與函數(shù)名之間有一個(gè)*號(hào)灼芭。
執(zhí)行Generator 函數(shù)返回一個(gè)指向函數(shù)內(nèi)部的指針對(duì)象戚扳,每次調(diào)用該對(duì)象的next方法,指針從函數(shù)頭部或上一次停下來(lái)的地方開(kāi)始執(zhí)行档泽,直到遇到下一個(gè)yield表達(dá)式/return語(yǔ)句攻泼。
next可傳入?yún)?shù),該參數(shù)會(huì)作為上一個(gè)yield表達(dá)式的返回值诡延。
例:
function* test() {
yield 'hello';
const text = yield 'world';
console.log(text);
return 'ending';
}
let t = test();
console.log(t.next());//{value: "hello", done: false}
console.log(t.next());//{value: "world", done: false}
console.log(t.next('!'));//"!"
//{value: "ending", done: true}
console.log(t.next());//{value: undefined, done: true}
假設(shè)有異步方法fetchData滞欠,如果我們希望通過(guò)Generator函數(shù)寫(xiě)出串聯(lián)執(zhí)行的效果:
function fetchData(data) {
return function (cb) {
setTimeout(function () {
cb(data + 1);
}, 500);
}
}
function* test() {
let t1 = yield fetchData(1);
let t2 = yield fetchData(t1);
}
let t = test();
t.next().value((data)=>{
t.next(data).value((data)=>{
t.next(data);
});
});
顯然不是我們想要的效果,這時(shí)可以做一個(gè)Generator自動(dòng)運(yùn)行方法肆良,通過(guò)遞歸解決:
run(test);//這樣一句話就夠了
function run(gen) {
const g = gen();
next();
function next() {
const result = g.next(...arguments);
if (!result.done) {
result.value(next);
}
}
}
但異步函數(shù)除了使用回調(diào)實(shí)現(xiàn)筛璧,還可以使用Promise實(shí)現(xiàn),這樣我們的自動(dòng)運(yùn)行方法就要加上對(duì)Promise的處理了惹恃。
function run(gen) {
const g = gen();
next();
function next() {
const result = g.next(...arguments);
if (!result.done) {
const value = result.value;
if(isPromise(value)){
value.then(next);
}else{
value(next);
}
}
}
function isPromise(obj){
if( obj && typeof obj.then == 'function' ) return true;
return false;
}
}
現(xiàn)在已經(jīng)完成了自動(dòng)執(zhí)行的功能隧哮,但存在兩個(gè)問(wèn)題。
- 不能獲取到Generator函數(shù)最終return的值座舍。
- 但如果異步操作中發(fā)生了異常沮翔,現(xiàn)在是沒(méi)辦法處理的。
為了解決這兩個(gè)問(wèn)題曲秉,需要作出修改
- run方法返回一個(gè)Promise采蚀,最終通過(guò)Promise來(lái)返回結(jié)果
- 遞歸調(diào)用next時(shí),加上onRejected進(jìn)行處理承二。而對(duì)于使用回調(diào)進(jìn)行異步的方法榆鼠,則將其轉(zhuǎn)換成一個(gè)Promise,使代碼可以復(fù)用亥鸠。
如果異步方法返回的不是promise妆够,則包裝成一個(gè)promise來(lái)處理识啦。
function toPromise(obj) {
if (isPromise(obj)) return obj;
if ('function' === typeof obj) return thunkToPromise(obj);
return obj;
}
function isPromise(obj) {
if (obj && typeof obj.then === 'function') return true;
return false;
}
function thunkToPromise(obj) {
return new Promise((resolve, reject) => {
//error first模式,回調(diào)函數(shù)接收的第一個(gè)參數(shù)為error
obj(function (err) {
if (err) return reject(err);
resolve([].splice.call(arguments, 1));
});
})
}
最后run方法返回一個(gè)promise神妹,這樣外部就可以獲取到自動(dòng)執(zhí)行中遇到的異惩窍或最終結(jié)果了。
function run(gen) {
return new Promise((resolve, reject) => {
const g = gen();
next();
function next() {
const result = g.next(...arguments);
if (!result.done) {
const promise = toPromise(result.value);
promise.then(next, reject);//遇到問(wèn)題就用reject返回異常鸵荠,不繼續(xù)遞歸
} else {
resolve(result.value);//返回最終執(zhí)行結(jié)果
}
}
})
}
async函數(shù)
async函數(shù)是ES2017標(biāo)準(zhǔn)引入的一種異步操作方式冕茅,在function前加上async使用。
例:
test();
async function test(){
let t1 = await fetchData('lin');
console.log(t1);//data of lin
let t2 = await fetchData('qibin');
console.log(t2);//data of qibin
}
function fetchData(name) {
return new Promise((resolve, reject) => {
setTimeout(function () {
resolve(`data of ${name}`);
}, 500);
})
}
很熟悉有沒(méi)有蛹找!其實(shí)async函數(shù)就是Generator+自動(dòng)執(zhí)行姨伤。
async異步操作超級(jí)優(yōu)雅,是我認(rèn)為js異步操作最好的形式庸疾。
更多關(guān)于Generator及async函數(shù)