快速掌握ES6 iterator Generator和async 之間的關係及其用法

1.遍歷器iterator

1.1 for遍歷

首先從遠古講起,剛出js的時候如何遍歷一個數組呢?javascript

var arr = [1, 2, 3, 4, 7, 8, 9]
    for (let i = 0;i < arr.length;i++) {
      console.log(arr[i]);
    }

1.2 forEach遍歷

看起來笨的一批,因此ES5給你研究了一個foreach方法,可是這個方法不能break,也不能改變數組自身的值,也不能返回任何值。html

var arr = [1, 2, 3, 4, 7, 8, 9]
    var arr2 = arr.forEach((element, index) => {
      console.log('第' + index + '個數值爲:' + element);
      return element * 2;
    });
    console.log(arr2) // undefined
		console.log(arr1) // [1, 2, 3, 4, 7, 8, 9]

因此說foreach只給你最基本的操做,其餘一律無論,若是你想要改變自身的值或者有break和countinue操做咱們可使用map操做,不展開講了,以前專門寫了篇博客總結了下。java

wzr:數組遍歷方法總結ios

1.3 for-in遍歷

那麼ES6專門爲遍歷數組提供了一種方法,就是for-of。說道for-of,不得不提到了for-inajax

那麼關於他們倆的區別,我也專門寫了一篇博客,不在這展開講了。axios

wzr:深刻理解枚舉屬性與for-in和for-of數組

值得一提的是for-in是能夠遍歷數組的,可是不推薦用for-in去遍歷數組,爲何呢?由於for -in 返回的可枚舉屬性是字符類型,不是數字類型,若是"0","1"這樣的屬性和1,2數組發生相加,極可能不是直接相加,而是字符串的疊加。例如promise

const items = [1,2,3,4]

for(item in items){
  let tempitem = item+1 
  console.log(items[tempitem]) // undefined
  console.log(tempitem) // 01 21 32 41 item與數字相加 會獲得字符串相加的結果,因此致使上面找不着items相應的項
}

因此爲了不歧義,仍是不要用for-in遍歷數組的好。瀏覽器

1.4 for-of

那麼接下來就進入正題了,由於for-in在數組這邊比較難用,因此ES6新添加的for-of來彌補for-in的不足。這是個正兒八經遍歷數組的方法。與 forEach() 不一樣的是,它支持 break、continue 和 return 語句。並且他自己的語法很是的簡單:異步

for (variable of iterable) {
    //statements
}
  • variable: 在每次迭代中,將不一樣屬性的值分配給變量。

  • iterable: 被迭代枚舉其屬性的對象。

並且關鍵的問題是for-of不只能夠遍歷數組,他能夠遍歷不少類數組對象。

  • Array
  • Map
  • Set
  • String
  • TypedArray
  • 函數的 arguments 對象
  • NodeList 對象

而他的原理在於這些類數組對象中都有一個屬性,就是Symbol.iterator,也就是說,只要帶Symbol.iterator他都能遍歷,咱們單獨把這個屬性拿出來,本身手動執行next()方法就會看到咱們成功的遍歷了這個數組:

const items = [1,2,3,4]

const giao =  items[Symbol.iterator]()

console.log(giao.next()) // {value: 1, done: false}
console.log(giao.next()) // {value: 2, done: false}
console.log(giao.next()) // {value: 3, done: false}
console.log(giao.next()) // {value: 4, done: false}
console.log(giao.next()) // {value: undefined, done: true}

同理,咱們能夠已經過手動寫一個iterator來更深刻的瞭解他的原理:

Array.prototype.myiterator = function(){
  let i = 0 
  let items = this
  return {
    next(){
      const done  = i >= items.length
      const value = done ? undefined : items[i++]
      return {
        value, done
      }
    }
  }
}

const item = [1,2,3,4]

// 控制檯
> const giao = item.myiterator() //當咱們得到到遍歷器時,咱們只須要代替for-of執行myiterator便可遍歷這個數組。
> giao.next()
{value: 1, done: false}
> giao.next()
{value: 2, done: false}
> giao.next()
{value: 3, done: false}
> giao.next()
{value: 4, done: false}
> giao.next()
{value: undefined, done: true}

效果跟for of是同樣的。另外值得注意的是,你能夠在任意對象裏添加這個屬性,讓他們可遍歷。

const items = [ 'blue', 'yellow', 'white', 'black']

for(item of items){
  console.log(item)
}

1.5總結

遍歷器若是存在與一個對象內,他就可讓這個對象可供for-of遍歷,for-of的遍歷方法就是不停地調用遍歷器的next()方法,直到done屬性變爲true

2.生成器 Generator

爲何拿生成器和遍歷器一塊兒講呢,由於本質上,生成器器函數返回的就是一個遍歷器!

生成器的語法很簡單,就是在function後面加個*,而後用yield來返回對應的值。(其實也能夠將yield能夠看作return,只不過須要next()來進行外調用,還有一個函數只能由一個return ,而yield能夠有多個)

function* items(){
	yield "1"
	yield "2"
	yield "3"
}

const num = items()

// 控制檯
> num.next()
{value: "1", done: false}
> num.next()
{value: "2", done: false}
> num.next()
{value: "3", done: false}
> num.next()
{value: undefined, done: true}
> num.next()
{value: undefined, done: true}

那麼咱們yield的以前一樣也能夠加入運算

function* items(){
	let i =0
	yield i // 0
	i++
	yield i // 1
	i++
	yield i // 2
	i++ //這個就不運行了,由於他在yield以後
}

const num = items()

//不用瀏覽器控制檯,直接打印也行
console.log(num.next()) // {value:0,done:false}
console.log(num.next()) // {value:1,done:false}
console.log(num.next()) // {value:2,done:false}
console.log(num.next()) // {value:undefined,done:true}

利用這樣的特性,咱們能夠用Generator來進行ajax的等待操做。

function ajax(url){
  // 請求成功自動調用next()方法。而後返回數據結果
  axios.get(url).then(res => gen.next(res.data))
}
function* step(){
  const class = yield ajax.get(`http://laotie.com/getclass`)
  const score = yield ajax.get(`http://laotie.com/getscore?name=${class[0].name})
}

// 得到這個函數的"遍歷器"
const gen = step()
// 啓動"遍歷器",不啓動就不會動
gen.next() // 獲取class
gen.next() // 獲取到score

由於第二個get請求依賴第一個請求的結果,因此咱們解決辦法第一個是運用Promise的回調來限制他們的前後順序,可是在咱們學習了生成器以後咱們發現生成器的特性很適合作這樣的事。也就是隻有當第一個請求執行完以後,才能順序執行第二個請求。

另外還有一些小的特性

*能夠添加到任意位置,都不會影響生成genterator。下面的寫法都是能夠的。

function * foo(x, y) { ··· }
function *foo(x, y) { ··· }
function* foo(x, y) { ··· }
function*foo(x, y) { ··· }

關於Generator的Thunk或者co模塊,由於ES8的async的加入,極大地簡化了Genrtator的操做,針對原理和實戰操做也就沒有那麼多要求了,接下來主要說一說async函數。

3.async

1.語法

準確的說,async就是Generator的語法糖,首先看他的語法。

async function laotie(){
  const data = await dosomething()
}

能夠看到,

  • 原來的*async代替
  • 原來的yieldawait代替

這樣作的直接的好處就是顯得更加語義化,可讀性更強。可是其實async作到的遠不止如此。

  1. 首先第一點,就是async不用不斷執行next()了,async函數內置了執行器,使得咱們在調用函數時,只須要直接調用便可。以下:

    // 接上一個代碼塊
     laotie()
  2. 如今async 的await仍是保留着等待的功能,可是由於沒有了next(),因此在調用await不會像yield那樣返回值了。在async中,只有return返回,並且返回的是一個promise對象。

    拿上面的代碼直接改寫成async加await的格式,

    async function items(){
    	let i =0
    	await i 
    	i++
    	await i 
    	i++
    	await i 
    	i++ 
    }
    
    console.log(items()) // Promise {<pending>}

    直接調用方法咱們能看到返回的是一個狀態爲resolved的Promise對象,而不是Iterator。

    而這個對象,返回的值就是函數裏return出來的值。咱們能夠用then()函數來接受這個值並打印他。

    async function items(){
    	let i =3
    	return i
    }
    
    items().then(res=>{
    	console.log(res) // 3
    })

    固然這麼舉例子準定不是正經的用法,這些例子主要用於區分Generator和async函數以前的區別。

2.用法

正確的用法如今是在await以後加入一個異步函數,await至關於將這個異步函數轉化爲同步函數,等這個異步函數執行完畢返回resolved的時候時候才往下執行進一步的操做。例如:

async function asyncPrint(value, ms) {
  await new Promise(resolve => {
    setTimeout(resolve, ms);
  });
  console.log(value);
}

asyncPrint('hello world', 1000); // 一秒後打印hellow world

若是這個async 的函數中間有多個await,那麼就讓多個await以排隊的方式執行。

用法2:

先讓咱們把以前generator的例子拿過來

function ajax(url){
  // 請求成功自動調用next()方法。而後返回數據結果
  axios.get(url).then(res => gen.next(res.data))
}
function* step(){
  const class = yield ajax.get(`http://laotie.com/getclass`)
  const score = yield ajax.get(`http://laotie.com/getscore?name=${class[0].name})
}

// 得到這個函數的遍歷器
const gen = step()
// 啓動遍歷器
gen.next()

寫着挺累的,可是async能夠快速的簡化它。由於await接受的就是一個Prmoise函數,因此咱們能夠直接在await後面使用axios,而後直接使用對象解構來獲取相應的值。

async function step(){
  const {data:{class}} = await axios.get(`http://laotie.com/getclass`)
  const {data:{core}} = await axios.get(`http://laotie.com/getscore?name=${class[0].name})
	return {class,core}
}

這樣是否是就方便多啦!

相關文章
相關標籤/搜索