// generator介紹:
function* hello() {
console.log("hello world")
}
hello();//沒有執行編程
// 直接調用hello不能像普通函數同樣打印輸出。promise
function* hello() {
console.log("hello world")
}
var h = hello();//僅僅建立了函數句柄,並無實際執行,須要進一步調用next()
h.next();//打印出了「hello world」多線程
function* hello() {
yield "hello";
yield "world";
return;
}異步
var h = hello();
h.next();//{value:'hello',done: false}
h.next();//{value: 'world', done: false}
h.next();//{value: undefined, done: true}異步編程
// 分析:上面引入了yield關鍵字:函數
// (1)建立了h對象,指向hello的句柄,線程
// (2)第一次調用next(),執行到"yield hello",暫緩執行,並返回了"hello"指針
// (3)第二次調用next(),繼續上一次的執行,執行到"yield world",暫緩執行,並返回了"world"。對象
// (4)第三次調用next(),直接執行return,並返回done:true,代表結束。ip
// 通過上面的分析,yield實際就是暫緩執行的標示,每執行一次next(),至關於指針移動到下一個yield位置。
// 總結一下,Generator函數是ES6提供的一種異步編程解決方案。經過yield標識位和next()方法調用,實現函數的分段執行。
function* gen(x, y) {
let z = yield x + y;
let res = yield z * 5;
return res;
}
var g = gen(5,6);
console.log(g.next());//{value: 11, done: false};
console.log(g.next());//{value: NAN, done:false};
console.log(g.next());//{value: undefined, done: true}
// 分析:
// (1)建立了g對象,指向gen的句柄,並傳入參數x=5,y=6
// (2)第一次調用next(),執行yield x+y, value值是11,後面尚未return,也沒有到最後的yield,因此沒退出
// (3)第二次調用next(),執行yield z*5, 爲何輸出是 NAN並非預想的55呢?由於將yield表達式的結果賦值給z以後,進行下一次next(),z的值並無保存。
// (4)第三次調用next(),遇到return,執行結束done:false
// 分析(3),z的值沒有保存,可是怎麼才能達到預期的55呢?改爲下面的程序段
function* gen(x, y) {
let z = yield x + y;
let res = yield z * 5;
return res;
}
var g = gen(5,6);
console.log(g.next());//{value: 11, done: false};
console.log(g.next(11));//{value: NAN, done:false};
console.log(g.next());//{value: undefined, done: true}
// 再執行第二次next()調用時,next的參數11能夠做爲yield中,參數11是上一次yield表達式的結果,也就是let z=yield x+y 變成了 let z=11;因此輸出了正確的結果
// 咱們不能每次都把計算好的結果寫到參數中,因此,修改程序段以下:
function* gen(x, y) {
let z = yield x + y;
let res = yield z * 5;
return res;
}
var g = gen(5,6);
let i = g.next();//i: {value: 11, done: false};
console.log(g.next(i.value()));//{value: NAN, done:false};
console.log(g.next());//{value: undefined, done: true}
// 最終執行第二次next()調用時,value值是預期的55。
// 總結:next()函數的參數做爲上一個yield表達式的結果。
function *gen() {
yield 1;
yield 2;
yield 3;
}
let g = gen();
g.next();//{value: 1, done: false}
g.next();//{value: 2, done: false}
g.return();//遇到return()函數,generat函數遍歷結束,{value: undifined, done: true},yield 3表達式並無執行
function *gen() {
yield 1;
yield 2;
yield 3;
}
let g = gen();
g.next();//{value: 1, done: false}
g.next();//{value: 2, done: false}
g.return(5);//遇到return()函數,generator函數遍歷結束,其參數5做爲結果的value值{value: 5, done: true},yield 3表達式並無執行
// yield表達式是generator函數暫緩執行的標誌,只能配合generator函數使用,用在普通函數中會報錯。
function gen(x,y){
yield 1;
yield 2;
yield 3;
}//Uncaught SyntaxError: Unexpected number
// yield*表達式的用法:
function *foo() {
yield 'a';
yield 'b';
}
function bar() {
yield 1;
yield 2;
yield foo();
yield 3;
}
var b = bar();
console.log(b.next());//{value: 1, done: false}
console.log(b.next());//{value: 2, done: false}
console.log(b.next());//{value: "a", done: false}
console.log(b.next());//{value: "b", done: false}
console.log(b.next());//{value: 3, done: false}
console.log(b.next());//{value: undefined, done: true}
// generator函數的應用:
// generator能夠模擬多線程之間的協做。
// 好比說A,B兩個線程根據實際邏輯控制共同完成某個任務,A運行一段時間後,暫緩執行,交由B運行,B運行一段時間後,再交回A運行,直到運行任務完成。
// 對於JavaScript單線程來講,咱們能夠理解爲函數間的協做,由多個函數間相互配合完成某個任務。
// Generator函數是ES6提供的一種異步編程解決方案,解決了異步編程的兩大問題:
// 回調地獄和異步控流
// 回調地獄與promise有關,不作介紹了
// 異步控流是什麼?異步操做之間,又能夠認爲成是同步的,上一個異步執行完以後,才能夠執行下一個異步程序,這時候須要一個函數來控制這個異步的流程。
// 若是task1完成了再作task2,而後交上task1,再交上task2。
// 如果以下:只能經過setTimeOut的時間控制實現異步。
function task1(next) {
setTimeout(function(){
console.log("Task1 done");
},100)
}
function task2(next) {
setTimeout(function(){
console.log("Task2 done");
},200)
}
function endTask1(next) {
setTimeout(function(){
console.log("send task1");
},300)
}
function endTask2(next) {
setTimeout(function(){
console.log("send task2");
},400)
}
task1()
task2()
endTask1()
endTask2()
// 可是若是執行完每一個步驟的時間相同甚至task1須要用時最多怎麼辦呢?
// 以下:
setTimeout(function() {
console.log("Task1 done");
setTimeout(function(){
console.log("Task2 done");
setTimeout(function(){
console.log("send task1");
// ....... 陷入了回調地獄
},500)
},500)
},500)
// 使用generat解決
function task1(next) {
setTimeout(function(){
console.log("Task1 done");
next();//完成後須要執行的下一件事
},500)
}
function task2(next) {
setTimeout(function(){
console.log("Task2 done");
next();//完成後須要執行的下一件事
},500)
}
function endTask1(next) {
setTimeout(function(){
console.log("send task1");
next();//完成後須要執行的下一件事
},500)
}
function endTask2(next) {
setTimeout(function(){
console.log("send task2");
next();//完成後須要執行的下一件事
},500)
}
function run(fn) {
let gen = fn();//fn:tack;fn(): task();
function next() {
let result = gen.next();//第一輪:執行yield task1;其中task1是個函數。因此,下面result.value是個函數。
if (result.done) {
return;
}
// 第一輪:result.value是個函數,表明了task1,參數傳入next函數名,實現了在task1中調用了run中的next函數,進而進行第二輪。
result.value(next);
};
next();
}
function* task() {
yield task1;
yield task2;
yield endTask1;
yield endTask2;
}
run(task);
// 控制檯每隔500ms分別輸出 "Task1 done" ,"Task2 done" ,"send task1" ,"send task2"