最近中文字幕高清中文字幕无,亚洲欧美高清一区二区三区,一本色道无码道dvd在线观看 ,一个人看的www免费高清中文字幕

全部開(kāi)發(fā)者教程

ES6-10 入門(mén)教程

ES6+ Generator 函數(shù)應(yīng)用

1. 前言

上一節(jié)我們注意學(xué)習(xí)了生成器的概念和基本用法,并通過(guò)兩個(gè)案例來(lái)說(shuō)明。但是生成器更加廣泛和設(shè)計(jì)之初是為了解決異步而產(chǎn)生的。我們會(huì)通過(guò)一個(gè)開(kāi)發(fā)中常見(jiàn)的問(wèn)題入手來(lái)看 生成器函數(shù)到底是怎么來(lái)解決異步調(diào)用的問(wèn)題,并且會(huì)實(shí)現(xiàn)一個(gè)簡(jiǎn)版的 co 庫(kù)。

2. 案例

在開(kāi)發(fā)過(guò)程中會(huì)遇到一個(gè)很常見(jiàn)的需求,我們想獲取一個(gè)值,但不能直接拿到,我們只能先請(qǐng)求一個(gè)接口如:api_1,獲取這個(gè)值的接口地址如:api_2。然后,請(qǐng)求 api_2 接口才能獲取這個(gè)值。這是一個(gè)典型的需要異步回調(diào)才能完成的功能。

在學(xué)習(xí) Promise 的時(shí)候我們也針對(duì)這樣的情況,我們可以使用 Promise 來(lái)完成這樣的功能:

var promise = function (url) {
	return new Promise((resolve, reject) => {
    ajax(url, (data) => {
      resolve(data)	// 成功
    }, (error) => {
      reject(error)	// 失敗
    })
  })
}
    
promise('api_1').then(res1 => {
  promise(res1).then(res2 => {
    console.log(res2)
  })
})

從上面的代碼中可以看出,在這種情況下,使用 Promise 好像并沒(méi)有解決回調(diào)地獄的問(wèn)題。那如何解決這種問(wèn)題呢?我們想到了 Generator 函數(shù)具有暫停功能,那是不是我們可以讓請(qǐng)求 api_2 接口時(shí)暫停,等到 api_1 請(qǐng)求成功獲取到地址后再去請(qǐng)求呢?按照這個(gè)思路我們可以有下面的代碼:

const ajax = function(api) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      if (api === 'api_1') {
        resolve('api_2');
      }
      if (api === 'api_2') {
        resolve(100);
      }
    }, 0)
  })
}

function * getValue() {
  const api = yield ajax('api_1');
  const value = yield ajax(api);
  return value;
}

console.log(getValue());	// Object [Generator] {}

上面的代碼是我們模擬 ajax 請(qǐng)求,通過(guò)使用生成器函數(shù)寫(xiě)出的代碼讓我們感覺(jué)有了同步的感覺(jué),但是這樣去執(zhí)行 getValue 函數(shù)是不會(huì)得到結(jié)果的。那么我們要怎樣去獲得結(jié)果呢?根據(jù)生成器函數(shù)的特點(diǎn),可以這樣寫(xiě):

let it = getValue();

let { value } = it.next();
value.then((data) => {
  let { value } = it.next(data);
  value.then((data) => {
    Console.log(data);
  });
});

從上面的代碼中看出還是有嵌套,好像并沒(méi)有解決問(wèn)題。但如果你細(xì)心,你會(huì)發(fā)現(xiàn)每個(gè)回調(diào)的邏輯基本都是一樣的。那么我們能不能對(duì)這樣的嵌套函數(shù)進(jìn)行封裝呢?答案當(dāng)然是可以的,有個(gè)庫(kù)就專(zhuān)門(mén)解決了這個(gè)痛點(diǎn) —— co 庫(kù),有興趣的可以去研究一下這個(gè)庫(kù),代碼很少,下面我們就來(lái)封裝一個(gè)這樣的庫(kù)。

先讓我們看看 co 庫(kù)是怎么使用的:

co(getValue()).then(res => {
  console.log(res);
})

從上面的代碼中可以看出,把函數(shù)傳入進(jìn)去,并讓函數(shù)執(zhí)行,然后在 then 的成功回調(diào)中可以獲取 getValue 函數(shù)返回的最終結(jié)果。這樣非常清晰地解決了上面我們需要手動(dòng)執(zhí)行的方法,下面我來(lái)分析一下具體的實(shí)現(xiàn)步驟:

  1. 從上面的用法可以看出,co 返回的是一個(gè) Promise 實(shí)例,所以我們需要返回一個(gè) new Promise() 實(shí)例;
  2. 傳入的生成器函數(shù)執(zhí)行后,我們可以調(diào)用 next () 函數(shù)拿到返回的值和是否執(zhí)行完的狀態(tài),判斷 done 如果是 true 時(shí)說(shuō)明執(zhí)行完了,可以執(zhí)行 resolve;
  3. 當(dāng)生成器函數(shù)沒(méi)有執(zhí)行完時(shí),這時(shí)我們就需要遞歸地去調(diào)用這個(gè) next () 來(lái)執(zhí)行下一步,因?yàn)閭魅氲闹凳且粋€(gè) Promise 實(shí)例,要想獲取它的結(jié)果就需要鏈?zhǔn)秸{(diào)用 then 方法,然后拿到結(jié)果進(jìn)行遞歸執(zhí)行。

有了上面的步驟分析,不難得到下面的代碼:

function co(it) {
  return new Promise((resolve, reject) => {
    function next(data) {
      let { value, done } = it.next(data);
      if (done) {
        resolve(value);
      } else {
        Promise.resolve(value).then(data => {
          next(data);
        }, reject)
      }
    }
    next(undefined);
  })
}

上面的代碼中需要注意的是,如果 yield 返回的不是一個(gè) Promise 對(duì)象時(shí),我們對(duì) value 使用了 Promise.resolve() 進(jìn)行了包裝,這樣就可以處理返回一個(gè)普通值時(shí)沒(méi)有 then 方法的問(wèn)題。

3. 小結(jié)

本節(jié)主要講解了 Generator 函數(shù)在異步中的應(yīng)用,解決了某些場(chǎng)景下還會(huì)產(chǎn)生回調(diào)地獄的問(wèn)題,通過(guò)封裝 co 方法讓我們的代碼寫(xiě)起來(lái)像是同步一樣,但是 Generator 函數(shù)還不是我們解決異步的終極方案,下一節(jié)我們將學(xué)習(xí) async 函數(shù),看它是怎么來(lái)解決異步的。