需要Promise原始碼版的朋友:傳送連結
本文主要講述(iterator)和生成器*/yield
之間的聯絡和各自的用法,以及生成器的高配版本async/await
的使用。
大綱:
- 迭代器(iterator)
- 生成器
*/yield
- 非同步版生成器
async/await
迭代器(iterator)
先瞅瞅“迭代”,這個詞是什麼意思呢?每一次“過程”的重複,稱之為迭代。不過迭代是會保留結果的,也就說每次都是以上一次迭代的結果為基準,開始下一次的迭代。舉個例子,迭代這個詞經常出現在產品開發之中,每個週期都會有產品的迭代開發,但是不可能每次都是從零開始做產品,肯定是基於上一版本的產品進行開發,也就是進行迭代。
從中我們可以整理出關於迭代的兩個關鍵點:
- 過程是重複的
- 返回上一次的迭代結果
那麼JS中的“迭代器”是個怎樣的概念呢?
檢視MDN中的概念:傳送地址
個人觀點:JS中的迭代器,就是一個陣列物件,不斷地呼叫
next
重複獲取過程,然後每次都返回一個結果。等到沒有東西可返回了,就終止。因此next
的返回物件有兩個屬性done
和value
。done
表示是否結束了,value
表示當前迭代的結果。當done
為true
的時候,表示迭代已結束,這時候是沒有返回結果的也就是沒有value
這個屬性。
然而迭代器是有一系列的規範的:
檢視MDN中的概念:傳送地址
迭代器
- 關於迭代器,就是我們上面討論的
next
方法,返回done
和value
(done:true
時可以省略)兩個引數。
function iteratorFunc(){
let arr=[...arguments]
let nIndex=0
return {
next:()=>{
return nIndex<arr.length?
{value:arr[nIndex++],done:false}:{done:true}
}
}
}
let a=iteratorFunc(1,2,3)
console.log(a.next())//{done:false,value:1}
console.log(a.next())//{done:false,value:2}
console.log(a.next())//{done:false,value:3}
console.log(a.next())//{done:true}
複製程式碼
可迭代“物件”
- 關於可迭代“物件”,我們需要再物件上實現
@@iterator
方法,也就是[Symbol.iterator]
,返回一個自定義的迭代方法,以表明這個物件是可以迭代的。有些JS內建的物件就是可迭代的,比如String,Array。
自帶的可迭代事例:
let str="我是歡樂的迭代器"
let b=str[Symbol.iterator]()
console.log(b.next())//{value: "我", done: false}
console.log(b.next())//{value: "是", done: false}
console.log(b.next())//{value: "歡", done: false}
複製程式碼
有沒有很神奇啊!用了這麼久的字串,居然還有這種操作。他的效果等同於上方的自定義迭代方法。那麼我們來寫個自定義的迭代方法:
str[Symbol.iterator] = function() {
return { // this is the iterator object, returning a single element, the string "bye"
next: function() {
this._index += 2
if (this._index<str.length) {
return { value: str[this._index], done: false };
} else {
return { done: true };
}
},
_index:-2
};
};
let c=str[Symbol.iterator]()
console.log(c.next())//{value: "我", done: false}
console.log(c.next())//{value: "歡", done: false}
console.log(c.next())//{value: "的", done: false}
console.log(c.next())//{value: "代", done: false}
console.log(c.next())//{done: true}
複製程式碼
這裡我寫的迭代器是返回一個隔一個字元。執行成功~yeah~
生成器(generator)
感覺寫迭代器還是很繞呢,於是出現了生成器(generator),專門幫我們生成迭代器的存在。
function * g(){}
let it= g()
console.log(it.next())//{value: undefined, done: true}
複製程式碼
看到熟悉的結構沒有!{value: undefined, done: true}
,不過我們沒有值。這個時候要向大家推薦*
的好基友yield
,一個yield
對應一個next
的值。
我們改寫下上方的字串的迭代器:
str[Symbol.iterator]= function * (){
let index=-2;
while(index<this.length){
index += 2
yield this[index]
}
}
let kk=str[Symbol.iterator]()
console.log(kk.next())//{value: "我", done: false}
console.log(kk.next())//{value: "歡", done: false}
console.log(kk.next())//{value: "的", done: false}
console.log(kk.next())//{value: "代", done: false}
複製程式碼
是不是方便了很多。
我們帶著幾個疑問來看看生成器:
yield
的返回值是啥?- 執行順序?
例項程式碼:
function * gy(){
console.log("zero")
let fisrt=yield "first"
console.log("fisrt",fisrt)
let second=yield "first"
console.log("second",second)
}
let ity= gy()
複製程式碼
第一次執行ity.next()
,只列印了zero
第二次執行ity.next()
,只列印了first undefined
第三次執行ity.next("third")
,只列印了second third
由此可見每次的next都止步於yield
,就不再執行下去了。yield
每次返回的都是當前ity.next(value)
的value
值。
async/await
我們來看看對於Promise這個物件的迭代器,我們該怎麼處理。也就是每個迭代器都是非同步的。
function setTime(value,id){
return new Promise((r,j)=>setTimeout(() => {
console.log(value)
r(id)
}, 10))
}
function *a(){
let r1 = yield setTime("first",1)
console.log(r1)
let r2 =yield setTime("second",2)
console.log(r2)
let r3 =yield setTime("third",3)
console.log(r3)
}
let k=a();
new Promise((resolve,reject)=>{
function next(data){
let {value,done}=k.next(data)
//k.next()返回一個promise,因此可以then
if(!done){
value.then((data)=>{
console.log(data)
next(data)
})
}
}
next();
})
複製程式碼
因為每個都是非同步的,所以需要我們二次處理,這個時候async/await
就可以出場了。只需要把*/yield無縫改成async/await即可。
async function a() {
let r1 = await setTime("first",1)
console.log(r1)
let r2 = await setTime("second",2)
console.log(r2)
let r3 = await setTime("third",3)
console.log(r3)
}
a()
複製程式碼