【理解ES7async/await並實現】手把手進行ES6非同步程式設計:Generator + Promise = Async/Await

AwesomeDevin發表於2019-04-19

概念

Generator 函式是 ES6 提供的一種非同步程式設計解決方案,執行 Generator 函式會返回一個遍歷器物件,也就是說,Generator 函式除了狀態機,還是一個遍歷器物件生成函式。返回的遍歷器物件,可以依次遍歷 Generator 函式內部的每一個狀態。

yield表示式

形式上,Generator 函式是一個普通函式,但是有兩個特徵。一是,function關鍵字與函式名之間有一個星號;二是,函式體內部使用yield表示式,定義不同的內部狀態。

function* foo(x) {
    yield 1
    yield 2
    yield 3
    yield 4
    return 5
}
複製程式碼

必須呼叫遍歷器物件的next方法,使得指標移向下一個狀態。也就是說,每次呼叫next方法,內部指標就從函式頭部或上一次停下來的地方開始執行,直到遇到下一個yield表示式(或return語句)為止。換言之,Generator 函式是分段執行的,yield表示式是暫停執行的標記,而next方法可以恢復執行。

function* foo() {
    yield 1
    yield 2
    return 3
}

var f = foo()
f.next()     //{ value: 1, done: false }
f.next().value     //2
f.next()     //{ value: 3, done: true}
f.next()     //{ value: undefined, done: true}
複製程式碼

Generator 函式已經執行完畢,next方法返回物件的value屬性為3,done屬性為true,之後再執行next(),done都為true,value未undefined

next方法的引數

yield表示式本身沒有返回值,或者說總是返回undefined。next方法可以帶一個引數,該引數就會被當作上一個yield表示式的返回值。

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

var a = foo(5);
a.next() // Object{value:6, done:false}
a.next() // Object{value:NaN, done:false}
a.next() // Object{value:NaN, done:true}

var b = foo(5);
b.next() // { value:6, done:false }
b.next(12) // { value:8, done:false }
b.next(13) // { value:42, done:true }
複製程式碼

Generator + Promise = 強大的非同步回撥方式

沒有回撥
function sayhello() {
    setTimeout(()=>{
        console.log(123)
    },3000)
}

function helloworld() {
    const data =  sayhello();
    console.log(data);
    console.log(456)
}
helloworld()
複製程式碼

這裡寫圖片描述

使用async/await

function sayhello(){
	return new Promise((resolve)=>{
		setTimeout(()=>{
			resolve(123)
	        console.log(123)
	    },3000)
	})
}

async helloworld(){
    const data = await sayhello()
    console.log(data)
    console.log(456)
}
複製程式碼
使用Generator + Promise 實現async/await
function co(gen) {
    if(!gen) return
	return new Promise((resolve,reject)=>{
		var it = gen();
		try{
			function step(next){
				if(next.done)
				{
					return resolve(next.value)
				}
				else{
					Promise.resolve(next.value).then((res)=>{
						return step(it.next(res))
					},(e)=>{
						return step(it.throw(e))
					})
				}
			}
			step(it.next())
		}
		catch(e){
			return reject(e)
		}
	})
}

function sayhello(){
	return new Promise((resolve)=>{
		setTimeout(()=>{
			resolve(123)
	        console.log(123)
	    },3000)
	})
}

co(
	function* helloworld(){
		const data = yield sayhello()
		console.log(data)
		console.log(456)
	}
)
複製程式碼

這裡寫圖片描述

可以看到,通過Generator + Promise(async/await)我們已經拿到了延時器中的資料。

任何複雜的非同步功能都可以被promise搞定,而且你還可以用generator把這些流程寫的像同步程式碼一樣。只要你讓yield返回一個promise。

相關文章