js es6深入應用系列(Generator)

盡是妖發表於2020-05-15

前言

generotor 和 普通函式的不同在於function 的時候加了一個*,

是的,我們看到es5的一個陌生關鍵字,yield,這個是不尋常的,為什麼這麼說呢?

這個在c#中,很常見的一個關鍵字,下面就來解釋一下js中的。

正文

function* gen() {
	yield "1";
	yield "2"
}
var iterator = gen();
console.log(iterator);
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());

這個玩意兒如果執行的話,會返回一個Iterator例項, 然後再執行Iterator例項的next()方法, 那麼這個函式才開始真正執行,
並把yield後面的值包裝成固定物件並返回,直到執行到函式結尾, 最後再返回undefined;

我為什麼這麼說呢?改一下程式碼:

function* gen() {
	yield "1";
	console.log('123');
	yield "2";
}
var iterator = gen();
console.log(iterator);
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next());

這個可以看圖說話我就不多說了。

下面介紹一下其他的一些特性。

yield*

yield* 表示式用於委託給另一個generator 或可迭代物件。

下面是委託為另一個:generator

function* foo() {
	yield 0;
	yield 1;
}
function* bar() {
	yield 'x';
	yield* foo();
	yield 'y';
}
for (let v of bar()) {
	console.log(v);
};

實際上上面就說了可以委託為任何可迭代的物件。

function* g3() {
  yield* [1, 2];
  yield* "34";
  yield* arguments;
}
var iterator = g3(5, 6);
console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: "3", done: false }
console.log(iterator.next()); // { value: "4", done: false }
console.log(iterator.next()); // { value: 5, done: false }
console.log(iterator.next()); // { value: 6, done: false }
console.log(iterator.next()); // { value: undefined, done: true }

return

The return() method returns the given value and finishes the generator.

function* gen() { 
  yield 1;
  yield 2;
  yield 3;
}
const g = gen();
g.next();        // { value: 1, done: false }
g.return('foo'); // { value: "foo", done: true }
g.next();        // { value: undefined, done: true }

throw()

The throw() method resumes the execution of a generator by throwing an error into it and returns an object with two properties done and value.

function* gen() {
	while (true) {
		try {
			yield 42;
		} catch (e) {
			console.log('Error caught!');
		}
	}
}

const g = gen();
g.next();
// { value: 42, done: false }
g.throw(new Error('Something went wrong'));
// "Error caught!"
// { value: 42, done: false }

next

next 分為帶引數和不帶引數,不帶引數的我就不說了,說下帶引數的。

function* foo(x) {
	var y = 2 * (yield (x + 1));
	console.log(y)
	var z = yield (y / 3);
	console.log(x);
	console.log(z);
        console.log(y);
	return (x + y + z);
}

var b = foo(5);
console.log(b.next()); // { value:6, done:false }
console.log(b.next(12)); // { value:8, done:false }
console.log(b.next(13)); // { value:42, done:true }

驚喜不驚喜意外不意外?

過程其實也很簡單,比如說第二個傳入了12。

這個執行過程其實是從第一個yiled 開始執行的,也就是這個12會替換(x + 1)

這樣y就等於24了,然後yield (y / 3) 就是8了。

後續也是這樣。

應用

<script>
	"use strict";
	function* main() {
		var result = yield request("http://www.filltext.com?rows=10&f={firstName}");
		console.log(result);
		//do 別的ajax請求;
	}
	function request(url) {
		var r = new XMLHttpRequest();
		r.open("GET", url, true);
		r.onreadystatechange = function () {
			if (r.readyState != 4 || r.status != 200) return;
			var data = JSON.parse(r.responseText);
			//資料成功返回以後, 程式碼就能夠繼續往下走了;
			it.next(data);
		};
		r.send();
	}
	var it = main();
	it.next();
	console.log("執行到這兒啦");
</script>

我把列印放出來:

其實上面可能存在疑惑的地方,那就是為什麼console.log(result);會列印出it.next(data);中data的結果。

其實非常簡單,當var result = yield request("http://www.filltext.com?rows=10&f={firstName}");執行完畢的時候,因為沒有next,然後就被阻塞了。

那麼更具上面next的特性。it.next(data)中的data會替換:yield request("http://www.filltext.com?rows=10&f={firstName}"),那麼result的結果就是data。

實現了我們async和await的效果。

相關文章