JavaScript異步 之 Generator函數

想寫js異步,但是我覺得這是很大一個工程。畢竟異步包含的東西太多了。慢慢歸納吧。這篇文章就是簡單介紹一下generator函數。
Generator 函數是 ES6 提供的一種異步編程解決方案。有人稱爲生成器。

  • 首先可以把它理解是一個狀態機,封裝了多個內部狀態。
  • 還是一個遍歷器對象生成函數。因爲執行 Generator 函數會返回一個遍歷器對象,返回的遍歷器對象,可以依次遍歷 Generator 函數內部的每一個狀態。

Generator 函數 兩個特徵

  1. function關鍵字與函數名之間有一個星號,星號位置沒有明確規定,下邊四個寫法都可以。
    在這裏插入圖片描述
  2. 函數體內部使用yield表達式,定義不同的內部狀態
    在這裏插入圖片描述

舉幾個例子分析一下Generator

看一下generator函數和普通函數的區別
在這裏插入圖片描述
正常函數調用之後就正常執行啦。
在這裏插入圖片描述
但是???generator函數莫得反應。

那怎麼才能讓它運行呢?
需要用到next方法。

返回一個遍歷器對象

function *greet() {
	console.log("Hello world!")
	yield 'hello----------yield'
	console.log("Hello world!111")
	yield 'world----------yield'
	console.log("Hello world!222")
	return "Hello world!"
}
let x = greet()
  • 1個next,輸出到Hello world!,也就是到第一個yield之前停止了
  • 2個next,輸出到Hello world!111,也就是到第二個yield之前停止了
  • 3個next,輸出到Hello world!222,不知道哪裏停止啦
  • 4個next,沒輸出了,就證明第三個next的時候已經執行完了

這個例子可以證明,generator函數會返回一個遍歷器,遍歷器調用next方法纔會執行函數體中的代碼。但是遇到yield會停止。
在這裏插入圖片描述
前邊說到過這麼一句話:可以把它理解是一個狀態機,封裝了多個內部狀態。執行 Generator 函數會返回一個遍歷器對象。 現在解釋了“會返回一個遍歷器對象”,但是封裝內部狀態的是什麼?

封裝了多個內部狀態

封裝了多個內部狀態就是yield和return代表其狀態。
每個next運行的時候,都是遇到yield就停止運行。如果沒有yield就會直接運行到結束。有return就返回return的值,沒有就返回undefined。
因此我函數調用寫四個next是沒用的嗷,因爲第三個的時候已經運行結束了。
我上邊的代碼裏也可以看到,yield和return後邊都是有內容的,那個內容就可以當作當前狀態,我們怎麼樣才能看到這個狀態呢?

function *greet() {
	yield 'hello----------yield'
	yield 'world----------yield'
	return "Hello world!"
}
let x = greet()
console.log(x.next())

直接打印x.next()即可:x.next()執行之後會返回一個對象,value值是yield之後的內容,done是布爾值,如果Generator 函數每執行完就是false,執行完了就是true。
在這裏插入圖片描述
那再思考一下:yield後邊只能跟字符串?接別的試試。

function *greet() {
	yield 1
	yield false
	yield null
	yield undefined
	yield [1,2,3]
	yield {name:'gen'}
}
let x = greet()
console.log(x.next())

下圖可以看出:

  • yield後邊跟什麼數據類型都可以
  • 函數運行完,沒有設置return,就返回undefined
    在這裏插入圖片描述
    那我接個函數試試
function *greet() {
	console.log(111)
	yield ()=>console.log("我是箭頭函數")
	yield (()=>console.log("我是調用的箭頭函數"))()
}
let x = greet()
console.log(x.next())
  • 第一個next執行到第一個yield前邊,並且返回值中的value是第一個yield後邊的內容。
  • 第二個next執行到第二個yield前邊,並且返回值中的value是第二個yield後邊的內容。
    • 因爲第二個yield後邊的函數調用了,並且沒返回值,所以value是undefined
  • 第三個next執行完整個函數,函數沒有返回值–>value是undefined。
    在這裏插入圖片描述

奇怪的問題增加了。 不是說執行到yield前邊碼?怎麼返回值是yield後邊的東西哦。
看下圖:let y = yield (()=>console.log(y))()拋出錯誤說我沒聲明y。這側面證明了。調用一個next方法,執行了yield後邊的代碼,但是沒執行yield前邊的代碼。
在這裏插入圖片描述
所以內部執行狀態應該是這樣的:遇到yield之後,會執行yield右邊緊跟着的內容,不執行yield左邊的內容,也不會執行yield下一行的內容。
yield就是一堵牆,牆右邊的和前邊的屬於一個世界,牆左邊和下邊的是一個世界。
第一個yield後邊無法輸出y,但是第二個yield就可輸出y了。也印證了我上邊說的話。
但是注意到沒,可以輸出y了,但是y是undefined,有沒有辦法讓它不是undefined?
在這裏插入圖片描述

當然可以,其實next方法可以傳值,用下邊的代碼實驗一下

function *foo() {
	var state1 = yield 'hello';
	console.log(state1)
	var state2 = yield 'world';
	console.log(state2)
}
let f = foo()
f.next('111')
f.next('222')
f.next(333)

在這裏插入圖片描述
神奇的事情發生了。輸出了222,333。也就是說輸出了第二個和第三個next的值。
其實仔細回想下,也沒有很神奇,都說了一牆之隔,第一個next只管到第一個yield的前邊和右邊,管不到左邊的state1的,更不用說給它賦值了。所以某個yield左邊的值,需要下一個next傳入參數。
在這裏插入圖片描述

一個有趣的題目

輸出兩行內容,第一行是"Do you love JavaScript?",第二行是"JavaScript loves you back ❤️"。

function *startGame() {
	const answer = yield "Do you love JavaScript?";
	if (answer !== "Yes") {
		return "Oh wow... Guess we're gone here";
	}
	return "JavaScript loves you back ❤️";
}
const game = startGame();

答案:

console.log( game.next().value ); 
// Do you love JavaScript?

console.log(game.next('Yes').value); 
// JavaScript loves you back ❤️
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章