国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

深入學習JavaScript系列(七)——Promise async/await generator

這篇具有很好參考價值的文章主要介紹了深入學習JavaScript系列(七)——Promise async/await generator。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

本篇屬于本系列第七篇

第一篇:#深入學習JavaScript系列(一)—— ES6中的JS執(zhí)行上下文

第二篇:# 深入學習JavaScript系列(二)——作用域和作用域鏈

第三篇:# 深入學習JavaScript系列(三)——this

第四篇:# 深入學習JavaScript系列(四)——JS閉包

第五篇:# 深入學習JavaScript系列(五)——原型/原型鏈

第六篇: # 深入學習JavaScript系列(六)——對象/繼承

第七篇:# 深入學習JavaScript系列(七)——Promise async/await generator

Promise屬于js進階的內(nèi)容,我剛剛開始學習的時候 我是這樣理解的: Promise是ES6中原生的一個方法,類似一個容器,代表著未來要發(fā)生的某件事情,屬于異步操作的一種方法,這句話在我初學的時候聽起來也是迷迷糊糊的 。

在閱讀了很多篇文章后,發(fā)現(xiàn)promise的底層原理:callback回調(diào)函數(shù)+發(fā)布訂閱模式

一 Promise

1.1 Promise概述

(1) 什么是promsie?

說的通俗易懂一點:從語法上講:Promise是一個對象(類),從它可以獲取異步操作的內(nèi)容消息;從本意上講,它是承諾,承諾它過一段時間會給你一種結果,Promise有三種狀態(tài):pending(等待),fulfiled(成功),rejected(失敗);狀態(tài)一旦改變,就不會再變,創(chuàng)造promise實例后,他會立即執(zhí)行

如果還不明白,我把他打印出來:

深入學習JavaScript系列(七)——Promise async/await generator

從上圖中可以看到 promise是一個對象,本身就有all reject,resolve等方法;還能看到promise的原型上有then,finally等方法。這幾個方法也是我們后面需要研究和實現(xiàn)的點。

(2) promise怎么用?

來個簡單的例子:

let p = new Promise(function(resolve, reject){
		//做一些異步操作
		setTimeout(function(){
			console.log('執(zhí)行完成Promise');
			resolve('要返回的數(shù)據(jù)可以任何數(shù)據(jù)例如接口返回數(shù)據(jù)');
		}, 2000);
	});

promise接受一個函數(shù)參數(shù),此函數(shù)中傳入兩個參數(shù):resolve,reject,分別表示異步操作執(zhí)行成功后的回調(diào)函數(shù)和異步操作執(zhí)行失敗后的回調(diào)函數(shù)。

但是我們?nèi)粘J褂貌皇沁@直接new 一個promise的,而是會在外面包裹一個函數(shù),然后調(diào)用外面的函數(shù),這樣做的好處是我們return出去的依然是一個promise對象,還可以使用promise上的then,catch等方法。

function promiseFn(){
    var p = new Promise(function(resolve, reject){
        //做一些異步操作
        setTimeout(function(){
            console.log('執(zhí)行完成');
            resolve('隨便什么數(shù)據(jù)');
        }, 2000);
    });
    return p;            
}
promiseFn()

promiseFn().then(function(data){
    console.log(data);
    // 其他操作
    
});

(3) promise有什么特點?

1、promise的狀態(tài)不受外界的影響,就像我開頭說的是一個容器,除了異步操作的結果其他手段無法改變promise的狀態(tài)。

2、狀態(tài)一旦改變 就不會改變,任何時候都會得到這個結果,狀態(tài)改變有兩種: 從pending變?yōu)閒ulfilled和從pending變?yōu)閞ejected.

(4) promise解決了什么問題?

promise解決了兩個主要的問題:

  • 解決回調(diào)地獄:代碼難以維護,嵌套過多每一個函數(shù)的輸出是第二個函數(shù)的輸入這種現(xiàn)象
  • 實現(xiàn)多個并發(fā)請求,獲取并發(fā)請求中的數(shù)據(jù),(解決異步問題)

在傳統(tǒng)的異步編程中,如果異步之間存在依賴關系,我們需要通過層層嵌套來實現(xiàn)這種依賴,如果嵌套層數(shù)過多,就會變成我們所說的回調(diào)地獄,promise可以把回調(diào)嵌套改成鏈式調(diào)用,

注: promise可以解決異步問題,但是不能說promise本身是異步的

通過上述幾個問題,應該基本能了解promsie了,下面就來說promsie中幾個重點

1.2 then

在上面的打印中我們可以知道 then是promsie原型鏈上的一個方法,
then其實就是解決promise中的回調(diào)問題的,promise通過then實現(xiàn)了鏈式調(diào)用,

then中有兩個參數(shù) :onFulfilled,onRejected 分別是成功的值,失敗的原因。

  • 當狀態(tài)state為fulfilled,則執(zhí)行onFulfilled,傳入this.value。當狀態(tài)state為rejected,則執(zhí)行onRejected,傳入this.reason

  • onFulfilled,onRejected如果他們是函數(shù),則必須分別在fulfilled,rejected后被調(diào)用,value或reason依次作為他們的第一個參數(shù)

then 的使用:

function promiseFn(){
    var p = new Promise(function(resolve, reject){
        //做一些異步操作
        setTimeout(function(){
            console.log('執(zhí)行完成');
            resolve('隨便什么數(shù)據(jù)');
        }, 2000);
    });
    return p;            
}
promiseFn()

promiseFn().then(function(data){
    console.log(data);
    // 其他操作
}).then(data=>{ 
console.log(data) //undefined 
return [1,2,3] 
}).then(data=>{ 
console.log(data) //[1,2,3]
})

那么then是怎么進行鏈式調(diào)用的呢?

  1. promise中使用.then()的方法來進行鏈式調(diào)用,通過.then()的回調(diào)拿到上一個.then()的返回值
  2. 鏈式調(diào)用要求.then()方法返回的必須是一個promise,這樣才有.then()方法;
  3. 需要按照順序執(zhí)行,所以只有當前的promise狀態(tài)變更后,才能執(zhí)行下一個then方法

1.3 如何手寫一個Promise

初步分析promise

首先我們來分析一個簡單的promise例子

const p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('result')
    },
    1000);
}) 

p1.then(res => console.log(res), err => console.log(err))

實際上 promise的整個執(zhí)行過程是:

  1. promise的構造方法接受一個executor(),在new Promise()時刻就立即執(zhí)行了executor()回調(diào),
  2. executor內(nèi)部的異步任務被放入宏/微任務隊列,等待執(zhí)行。也就是上面代碼中的定時器。
  3. 當then()被執(zhí)行時,收集回調(diào)(成功或者失?。?同時放入成功或者失敗隊列。
  4. executor ()異步任務被執(zhí)行,觸發(fā) resolve/reject 從成功/失敗中取出回調(diào)依次執(zhí)行

通過上面講解執(zhí)行過程可以看出 promise使用的是個觀察者模式,也就是收集依賴 -觸發(fā)通知-取出依賴執(zhí)行的方式

總結:在promise中 執(zhí)行順序是then收集依賴 -異步觸發(fā)resolve - resolve收集依賴

實現(xiàn)promise

在1.1中提到 promise本質(zhì)上是一個類,所以使用class來聲明promise

class MyPromise {
    // 構造方法接收一個回調(diào)
    constructor(executor) {
      this._resolveQueue = []    // then收集的執(zhí)行成功的回調(diào)隊列
      this._rejectQueue = []     // then收集的執(zhí)行失敗的回調(diào)隊列
  
      // 由于resolve/reject是在executor內(nèi)部被調(diào)用,
      // 因此需要使用箭頭函數(shù)固定this指向, 否則找不到this._resolveQueue
      let _resolve = (val) => {
        // 從成功隊列里取出回調(diào)依次執(zhí)行
        while(this._resolveQueue.length) {
          const callback = this._resolveQueue.shift()
          callback(val)
        }
      }
      // 實現(xiàn)同resolve
      let _reject = (val) => {
        while(this._rejectQueue.length) {
          const callback = this._rejectQueue.shift()
          callback(val)
        }
      }
      // new Promise()時立即執(zhí)行executor,并傳入resolve和reject
      executor(_resolve, _reject)
    }
  
    // then方法,接收一個成功的回調(diào)和一個失敗的回調(diào),并push進對應隊列
    then(resolveFn, rejectFn) {
      this._resolveQueue.push(resolveFn)
      this._rejectQueue.push(rejectFn)
    }
  }

在上面的代碼中 我們通過構造方法 創(chuàng)建了兩個隊列:resolveQueue rejectQueue 用來收集執(zhí)行成功/失敗后的回調(diào)隊列, 然后依次判斷并取出作為resolve和reject的回調(diào) 最后在調(diào)用then方法的時候返回即可,著就是promise的基礎功能 實現(xiàn)了resolve 和rejcet

實現(xiàn)promiseA+

當然 我們還要繼續(xù)往下寫:在Es6中 Promise需要遵循 PromiseA+規(guī)范,要求對promise的狀態(tài)進行控制 。

PromiseA+規(guī)范很多,核心規(guī)則有兩個:

  1. Promise的狀態(tài)有且只有三種: Pending(等待)。Fulfilled(已完成)。 Rejected(拒絕)。
  2. then有兩個參數(shù)可選,分別對應狀態(tài)改變時觸發(fā)的回調(diào),then方法返回一個promise 同時then方法可以被同一個promise調(diào)用很多次

加上promiseA+規(guī)范后,我們的代碼是這樣的:

//Promise/A+規(guī)范的三種狀態(tài)
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

class MyPromise {
  // 構造方法接收一個回調(diào)
  constructor(executor) {
    this._status = PENDING     // Promise狀態(tài) 初始的時候
    this._resolveQueue = []    // 成功隊列, resolve時觸發(fā)
    this._rejectQueue = []     // 失敗隊列, reject時觸發(fā)

    // 由于resolve/reject是在executor內(nèi)部被調(diào)用, 因此需要使用箭頭函數(shù)固定this指向, 否則找不到this._resolveQueue
    let _resolve = (val) => {
      if(this._status !== PENDING) return   // 對應規(guī)范中的"狀態(tài)只能由pending到fulfilled或rejected"
      this._status = FULFILLED              // 變更狀態(tài)

      // 這里之所以使用一個隊列來儲存回調(diào),是為了實現(xiàn)規(guī)范要求的 "then 方法可以被同一個 promise 調(diào)用多次"
      // 如果使用一個變量而非隊列來儲存回調(diào),那么即使多次p1.then()也只會執(zhí)行一次回調(diào)
      while(this._resolveQueue.length) {    
        const callback = this._resolveQueue.shift()
        callback(val)
      }
    }
    // 實現(xiàn)同resolve
    let _reject = (val) => {
      if(this._status !== PENDING) return   // 對應規(guī)范中的"狀態(tài)只能由pending到fulfilled或rejected"
      this._status = REJECTED               // 變更狀態(tài)
      while(this._rejectQueue.length) {
        const callback = this._rejectQueue.shift()
        callback(val)
      }
    }
    // new Promise()時立即執(zhí)行executor,并傳入resolve和reject
    executor(_resolve, _reject)
  }

  // then方法,接收一個成功的回調(diào)和一個失敗的回調(diào)
  then(resolveFn, rejectFn) {
    this._resolveQueue.push(resolveFn)
    this._rejectQueue.push(rejectFn)
  }
}

解釋:

  1. 先初始化的時候增加三種狀態(tài)
  2. 在進入constructor構造函數(shù)時把狀態(tài)設置為PENDING
  3. 在執(zhí)行_resolve 和_reject時需要判斷當前狀態(tài)是否為PENDING,如果不是直接return出去
  4. 在進入_resolve 和_reject流程后把狀態(tài)對應改為FULFILLED 和REJECTED。

實現(xiàn)then方法

接下來到了 鏈式調(diào)用;上面我們提到 promsie采用了鏈式調(diào)用的方法來改變回調(diào)地獄
前面分析過鏈式調(diào)用的步驟:

  1. promise中使用.then()的方法來進行鏈式調(diào)用,通過.then()的回調(diào)拿到上一個.then()的返回值
  2. 鏈式調(diào)用要求.then()方法返回的必須是一個promise,這樣才有.then()方法;
  3. 需要按照順序執(zhí)行,所以只有當前的promise狀態(tài)變更后,才能執(zhí)行下一個then方法

接下來就按照這個步驟實現(xiàn)then

注意: 在使用then方法的時候,我們需要判斷 傳入的參數(shù)是否是function;同時要判斷promise的三種狀態(tài)對應不同的寫法

// then方法,接收一個成功的回調(diào)和一個失敗的回調(diào)
  then(resolveFn, rejectFn) {
    // 根據(jù)規(guī)范,如果then的參數(shù)不是function,則我們需要忽略它, 讓鏈式調(diào)用繼續(xù)往下執(zhí)行
    typeof resolveFn !== 'function' ? resolveFn = value => value : null
    typeof rejectFn !== 'function' ? rejectFn = reason => {
      throw new Error(reason instanceof Error? reason.message:reason);
    } : null
  
    // return一個新的promise
    return new MyPromise((resolve, reject) => {
      // 把resolveFn重新包裝一下,再push進resolve執(zhí)行隊列,這是為了能夠獲取回調(diào)的返回值進行分類討論
      const fulfilledFn = value => {
        try {
          // 執(zhí)行第一個(當前的)Promise的成功回調(diào),并獲取返回值
          let x = resolveFn(value)
          // 分類討論返回值,如果是Promise,那么等待Promise狀態(tài)變更,否則直接resolve
          x instanceof MyPromise ? x.then(resolve, reject) : resolve(x)
        } catch (error) {
          reject(error)
        }
      }
  
      // reject同理
      const rejectedFn  = error => {
        try {
          let x = rejectFn(error)
          x instanceof MyPromise ? x.then(resolve, reject) : resolve(x)
        } catch (error) {
          reject(error)
        }
      }
  
      switch (this._status) {
        // 當狀態(tài)為pending時,把then回調(diào)push進resolve/reject執(zhí)行隊列,等待執(zhí)行
        case PENDING:
          this._resolveQueue.push(fulfilledFn)
          this._rejectQueue.push(rejectedFn)
          break;
        // 當狀態(tài)已經(jīng)變?yōu)閞esolve/reject時,直接執(zhí)行then回調(diào)
        case FULFILLED:
          fulfilledFn(this._value)    // this._value是上一個then回調(diào)return的值(見完整版代碼)
          break;
        case REJECTED:
          rejectedFn(this._value)
          break;
      }
    })
  }

promise+then實現(xiàn)

//Promise/A+規(guī)定的三種狀態(tài)
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

class MyPromise {
  // 構造方法接收一個回調(diào)
  constructor(executor) {
    this._status = PENDING     // Promise狀態(tài)
    this._value = undefined    // 儲存then回調(diào)return的值
    this._resolveQueue = []    // 成功隊列, resolve時觸發(fā)
    this._rejectQueue = []     // 失敗隊列, reject時觸發(fā)

    // 由于resolve/reject是在executor內(nèi)部被調(diào)用, 因此需要使用箭頭函數(shù)固定this指向, 否則找不到this._resolveQueue
    let _resolve = (val) => {
      //把resolve執(zhí)行回調(diào)的操作封裝成一個函數(shù),放進setTimeout里,以兼容executor是同步代碼的情況
      const run = () => {
        if(this._status !== PENDING) return   // 對應規(guī)范中的"狀態(tài)只能由pending到fulfilled或rejected"
        this._status = FULFILLED              // 變更狀態(tài)
        this._value = val                     // 儲存當前value

        // 這里之所以使用一個隊列來儲存回調(diào),是為了實現(xiàn)規(guī)范要求的 "then 方法可以被同一個 promise 調(diào)用多次"
        // 如果使用一個變量而非隊列來儲存回調(diào),那么即使多次p1.then()也只會執(zhí)行一次回調(diào)
        while(this._resolveQueue.length) {    
          const callback = this._resolveQueue.shift()
          callback(val)
        }
      }
      setTimeout(run)
    }
    // 實現(xiàn)同resolve
    let _reject = (val) => {
      const run = () => {
        if(this._status !== PENDING) return   // 對應規(guī)范中的"狀態(tài)只能由pending到fulfilled或rejected"
        this._status = REJECTED               // 變更狀態(tài)
        this._value = val                     // 儲存當前value
        while(this._rejectQueue.length) {
          const callback = this._rejectQueue.shift()
          callback(val)
        }
      }
      // 這里的定時器是為了 保證promise的執(zhí)行順序 下面的也是
      setTimeout(run)
    }
    // new Promise()時立即執(zhí)行executor,并傳入resolve和reject
    executor(_resolve, _reject)
  }

  // then方法,接收一個成功的回調(diào)和一個失敗的回調(diào)
  then(resolveFn, rejectFn) {
    // 根據(jù)規(guī)范,如果then的參數(shù)不是function,則我們需要忽略它, 讓鏈式調(diào)用繼續(xù)往下執(zhí)行
    typeof resolveFn !== 'function' ? resolveFn = value => value : null
    typeof rejectFn !== 'function' ? rejectFn = reason => {
      throw new Error(reason instanceof Error? reason.message:reason);
    } : null
  
    // return一個新的promise
    return new MyPromise((resolve, reject) => {
      // 把resolveFn重新包裝一下,再push進resolve執(zhí)行隊列,這是為了能夠獲取回調(diào)的返回值進行分類討論
      const fulfilledFn = value => {
        try {
          // 執(zhí)行第一個(當前的)Promise的成功回調(diào),并獲取返回值
          let x = resolveFn(value)
          // 分類討論返回值,如果是Promise,那么等待Promise狀態(tài)變更,否則直接resolve
          x instanceof MyPromise ? x.then(resolve, reject) : resolve(x)
        } catch (error) {
          reject(error)
        }
      }
  
      // reject同理
      const rejectedFn  = error => {
        try {
          let x = rejectFn(error)
          x instanceof MyPromise ? x.then(resolve, reject) : resolve(x)
        } catch (error) {
          reject(error)
        }
      }
  
      switch (this._status) {
        // 當狀態(tài)為pending時,把then回調(diào)push進resolve/reject執(zhí)行隊列,等待執(zhí)行
        case PENDING:
          this._resolveQueue.push(fulfilledFn)
          this._rejectQueue.push(rejectedFn)
          break;
        // 當狀態(tài)已經(jīng)變?yōu)閞esolve/reject時,直接執(zhí)行then回調(diào)
        case FULFILLED:
          fulfilledFn(this._value)    // this._value是上一個then回調(diào)return的值(見完整版代碼)
          break;
        case REJECTED:
          rejectedFn(this._value)
          break;
      }
    })
  }
}

注:最后的實現(xiàn)和上面兩個相比加了定時器的處理,注意仔細查看,是為了處理promise的返回順序。

promise的其他方法實現(xiàn)

promise.prototype.catch()

catch()方法返回一個promise 并且處理拒絕的情況

//catch方法其實就是執(zhí)行一下then的第二個回調(diào)
catch(rejectFn) {
  return this.then(undefined, rejectFn)
}

promise.prototype.finally()

finally()方法返回一個promise 在promise結束后 ,無論結果是fulfilled或者 rejected 都會執(zhí)行指定的回調(diào)函數(shù), 在finally之后 花可以繼續(xù)使用then,

//finally方法
finally(callback) {
    return this.then(
      value => MyPromise.resolve(callback()).then(() => value),  
      // MyPromise.resolve執(zhí)行回調(diào),并在then中return結果傳遞給后面的Promise
      reason => MyPromise.resolve(callback()).then(() => { throw reason }) 
      // reject同理
    )
  }
  

promise.resolve()
promise.resolve(value)方法返回一個已給定值解析后的promise對象 如果該值為promise 返回這個promise;如果這個值為then方法, 返回的promise會跟隨這個方法的對象,采用它的最終狀態(tài),否則返回的promise將以此值完成。

//靜態(tài)的resolve方法
static resolve(value) {
  if(value instanceof MyPromise) return value // 根據(jù)規(guī)范, 如果參數(shù)是Promise實例, 直接return這個實例
  return new MyPromise(resolve => resolve(value))
}

Promise.reject()

Promise.reject()方法返回一個帶有拒絕原因的Promise對象。

//靜態(tài)的reject方法
static reject(reason) {
  return new MyPromise((resolve, reject) => reject(reason))
}
復制代碼

Promise.all()

Promise.all(iterable)方法返回一個 Promise 實例,此實例在 iterable 參數(shù)內(nèi)所有的 promise 都“完成(resolved)”或參數(shù)中不包含 promise 時回調(diào)完成(resolve);如果參數(shù)中 promise 有一個失?。╮ejected),此實例回調(diào)失?。╮eject),失敗原因的是第一個失敗 promise 的結果。

//靜態(tài)的all方法
static all(promiseArr) {
  let index = 0
  let result = []
  return new MyPromise((resolve, reject) => {
    promiseArr.forEach((p, i) => {
      //Promise.resolve(p)用于處理傳入值不為Promise的情況
      MyPromise.resolve(p).then(
        val => {
          index++
          result[i] = val
          //所有then執(zhí)行后, resolve結果
          if(index === promiseArr.length) {
            resolve(result)
          }
        },
        err => {
          //有一個Promise被reject時,MyPromise的狀態(tài)變?yōu)閞eject
          reject(err)
        }
      )
    })
  })
}
復制代碼

Promise.race()

Promise.race(iterable)方法返回一個 promise,一旦迭代器中的某個promise解決或拒絕,返回的 promise就會解決或拒絕。

static race(promiseArr) {
  return new MyPromise((resolve, reject) => {
    //同時執(zhí)行Promise,如果有一個Promise的狀態(tài)發(fā)生改變,就變更新MyPromise的狀態(tài)
    for (let p of promiseArr) {
      MyPromise.resolve(p).then(  //Promise.resolve(p)用于處理傳入值不為Promise的情況
        value => {
          resolve(value)        //注意這個resolve是上邊new MyPromise的
        },
        err => {
          reject(err)
        }
      )
    }
  })
}

完整promise代碼

//Promise/A+規(guī)定的三種狀態(tài)
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

class MyPromise {
  // 構造方法接收一個回調(diào)
  constructor(executor) {
    this._status = PENDING     // Promise狀態(tài)
    this._value = undefined    // 儲存then回調(diào)return的值
    this._resolveQueue = []    // 成功隊列, resolve時觸發(fā)
    this._rejectQueue = []     // 失敗隊列, reject時觸發(fā)

    // 由于resolve/reject是在executor內(nèi)部被調(diào)用, 因此需要使用箭頭函數(shù)固定this指向, 否則找不到this._resolveQueue
    let _resolve = (val) => {
      //把resolve執(zhí)行回調(diào)的操作封裝成一個函數(shù),放進setTimeout里,以兼容executor是同步代碼的情況
      const run = () => {
        if(this._status !== PENDING) return   // 對應規(guī)范中的"狀態(tài)只能由pending到fulfilled或rejected"
        this._status = FULFILLED              // 變更狀態(tài)
        this._value = val                     // 儲存當前value

        // 這里之所以使用一個隊列來儲存回調(diào),是為了實現(xiàn)規(guī)范要求的 "then 方法可以被同一個 promise 調(diào)用多次"
        // 如果使用一個變量而非隊列來儲存回調(diào),那么即使多次p1.then()也只會執(zhí)行一次回調(diào)
        while(this._resolveQueue.length) {    
          const callback = this._resolveQueue.shift()
          callback(val)
        }
      }
      setTimeout(run)
    }
    // 實現(xiàn)同resolve
    let _reject = (val) => {
      const run = () => {
        if(this._status !== PENDING) return   // 對應規(guī)范中的"狀態(tài)只能由pending到fulfilled或rejected"
        this._status = REJECTED               // 變更狀態(tài)
        this._value = val                     // 儲存當前value
        while(this._rejectQueue.length) {
          const callback = this._rejectQueue.shift()
          callback(val)
        }
      }
      setTimeout(run)
    }
    // new Promise()時立即執(zhí)行executor,并傳入resolve和reject
    executor(_resolve, _reject)
  }

  // then方法,接收一個成功的回調(diào)和一個失敗的回調(diào)
  then(resolveFn, rejectFn) {
    // 根據(jù)規(guī)范,如果then的參數(shù)不是function,則我們需要忽略它, 讓鏈式調(diào)用繼續(xù)往下執(zhí)行
    typeof resolveFn !== 'function' ? resolveFn = value => value : null
    typeof rejectFn !== 'function' ? rejectFn = reason => {
      throw new Error(reason instanceof Error? reason.message:reason);
    } : null
  
    // return一個新的promise
    return new MyPromise((resolve, reject) => {
      // 把resolveFn重新包裝一下,再push進resolve執(zhí)行隊列,這是為了能夠獲取回調(diào)的返回值進行分類討論
      const fulfilledFn = value => {
        try {
          // 執(zhí)行第一個(當前的)Promise的成功回調(diào),并獲取返回值
          let x = resolveFn(value)
          // 分類討論返回值,如果是Promise,那么等待Promise狀態(tài)變更,否則直接resolve
          x instanceof MyPromise ? x.then(resolve, reject) : resolve(x)
        } catch (error) {
          reject(error)
        }
      }
  
      // reject同理
      const rejectedFn  = error => {
        try {
          let x = rejectFn(error)
          x instanceof MyPromise ? x.then(resolve, reject) : resolve(x)
        } catch (error) {
          reject(error)
        }
      }
  
      switch (this._status) {
        // 當狀態(tài)為pending時,把then回調(diào)push進resolve/reject執(zhí)行隊列,等待執(zhí)行
        case PENDING:
          this._resolveQueue.push(fulfilledFn)
          this._rejectQueue.push(rejectedFn)
          break;
        // 當狀態(tài)已經(jīng)變?yōu)閞esolve/reject時,直接執(zhí)行then回調(diào)
        case FULFILLED:
          fulfilledFn(this._value)    // this._value是上一個then回調(diào)return的值(見完整版代碼)
          break;
        case REJECTED:
          rejectedFn(this._value)
          break;
      }
    })
  }

  //catch方法其實就是執(zhí)行一下then的第二個回調(diào)
  catch(rejectFn) {
    return this.then(undefined, rejectFn)
  }

  //finally方法
  finally(callback) {
    return this.then(
      value => MyPromise.resolve(callback()).then(() => value),             //執(zhí)行回調(diào),并returnvalue傳遞給后面的then
      reason => MyPromise.resolve(callback()).then(() => { throw reason })  //reject同理
    )
  }

  //靜態(tài)的resolve方法
  static resolve(value) {
    if(value instanceof MyPromise) return value //根據(jù)規(guī)范, 如果參數(shù)是Promise實例, 直接return這個實例
    return new MyPromise(resolve => resolve(value))
  }

  //靜態(tài)的reject方法
  static reject(reason) {
    return new MyPromise((resolve, reject) => reject(reason))
  }

  //靜態(tài)的all方法
  static all(promiseArr) {
    let index = 0
    let result = []
    return new MyPromise((resolve, reject) => {
      promiseArr.forEach((p, i) => {
        //Promise.resolve(p)用于處理傳入值不為Promise的情況
        MyPromise.resolve(p).then(
          val => {
            index++
            result[i] = val
            if(index === promiseArr.length) {
              resolve(result)
            }
          },
          err => {
            reject(err)
          }
        )
      })
    })
  }

  //靜態(tài)的race方法
  static race(promiseArr) {
    return new MyPromise((resolve, reject) => {
      //同時執(zhí)行Promise,如果有一個Promise的狀態(tài)發(fā)生改變,就變更新MyPromise的狀態(tài)
      for (let p of promiseArr) {
        MyPromise.resolve(p).then(  //Promise.resolve(p)用于處理傳入值不為Promise的情況
          value => {
            resolve(value)        //注意這個resolve是上邊new MyPromise的
          },
          err => {
            reject(err)
          }
        )
      }
    })
  }
}

二 Generator

ES6 新引入Generator函數(shù),也可以說是一種數(shù)據(jù)類型;最大的特點就是暫停執(zhí)行。

關鍵詞是function后面有一個*,

Generato原理

1、 Generator可以通過yield關鍵字,把函數(shù)的執(zhí)行流掛起,掛起的函數(shù)不會馬上就執(zhí)行,而是返回一個指向內(nèi)部狀態(tài)的指針對象;

2、 這個對象是一個遍歷器(iterator)對象,iterator對象上有一個next()方法。

3、通過調(diào)用next()方法切換到下一個狀態(tài),移動內(nèi)部指針,使得指針指向下一個狀態(tài)。

4、next()同時會返回一個對象,表示當前階段的信息- 其中 value 屬性是 yield 語句后面表達式的值,表示當前階段的值;done 屬性表示 Generator 函數(shù)是否執(zhí)行完畢,即是否還有下一個階段

5、 返回遍歷器對象有個 throw 方法可以拋出錯誤,拋出的錯誤可以被函數(shù)體內(nèi)的 try/catch 代碼塊捕獲

:yield 只能在 Generator 中使用,在其他地方使用會報錯

function* myGenerator() {
  yield '1'
  yield '2'
  return '3'
}

const gen = myGenerator();  // 獲取迭代器
gen.next()  //{value: "1", done: false}
gen.next()  //{value: "2", done: false}
gen.next()  //{value: "3", done: true}

也可以通過給next()傳參, 讓yield具有返回值

function* myGenerator() {
  console.log(yield '1')  //test1
  console.log(yield '2')  //test2
  console.log(yield '3')  //test3
}

// 獲取迭代器
const gen = myGenerator();

gen.next()
gen.next('test1')
gen.next('test2')
gen.next('test3')

那么具體Generator是怎么執(zhí)行的呢?
這里參考:鏈接
中方法,轉(zhuǎn)化為es5 方法后代碼如下:

"use strict";

var _marked =
/*#__PURE__*/
// mark()是內(nèi)置的一個方法 為生成器綁定了一系列的原型
regeneratorRuntime.mark(foo);

function foo() {
// wrap() return一個方法
  return regeneratorRuntime.wrap(function foo$(_context) {
    while (1) {
      switch (_context.prev = _context.next) {
        case 0:
          _context.next = 2;
          return 'result1';

        case 2:
          _context.next = 4;
          return 'result2';

        case 4:
          _context.next = 6;
          return 'result3';

        case 6:
        case "end":
          return _context.stop();
      }
    }
  }, _marked);
}

var gen = foo();
console.log(gen.next().value);
console.log(gen.next().value);
console.log(gen.next().value);

分析一下上面的代碼會發(fā)現(xiàn),Generator的實現(xiàn)核心在于用一個context對象儲存上下文,

每一次yield的時候;函數(shù)并沒有被真正掛起,在執(zhí)行過程中都傳入一遍生成器函數(shù),然后用context存儲上下文,每次執(zhí)行生成器函數(shù)的時候,都可以從上一個執(zhí)行結果開始執(zhí)行,看上去就像函數(shù)被掛起一樣。

調(diào)用流程如下:

  • 我們定義的function* 生成器函數(shù)被轉(zhuǎn)化為以上代碼

  • 轉(zhuǎn)化后的代碼分為三大塊:

    • gen$(_context)由yield分割生成器函數(shù)代碼而來
    • context對象用于儲存函數(shù)執(zhí)行上下文
    • invoke()方法定義next(),用于執(zhí)行gen$(_context)來跳到下一步
  • 當我們調(diào)用g.next(),就相當于調(diào)用invoke()方法,執(zhí)行gen$(_context),進入switch語句,switch根據(jù)context的標識,執(zhí)行對應的case塊,return對應結果

  • 當生成器函數(shù)運行到末尾(沒有下一個yield或已經(jīng)return),switch匹配不到對應代碼塊,就會return空值,這時g.next()返回{value: undefined, done: true}

二 async/await

async/await實現(xiàn)

首先 async/await實際上是對Generator的封裝,是一個語法糖 我們對Generator不熟悉的原因是出現(xiàn)不久后就被更方便的async/await所取代了

我們在Generator章節(jié)分析到,Generator中yiled的用法和async/await的用法有很多相同的地方,都提供了暫停執(zhí)行的功能,但是具體的不同有三點:

  1. async/await自帶執(zhí)行器 不需要手動next()就可以執(zhí)行下一步
  2. async函數(shù)返回的值直接是一個promise對象,Genreator返回的是生成器對象
  3. await能夠返回Promise的resolve/reject的值

所以我們在實現(xiàn)async/await時 也是在Generator上增加上面三點功能

自動執(zhí)行

在Generator中 手動執(zhí)行如下代碼:

function* myGenerator() {
  yield Promise.resolve(1);
  yield Promise.resolve(2);
  yield Promise.resolve(3);
}

// 手動執(zhí)行迭代器
const gen = myGenerator()
gen.next().value.then(val => {
  console.log(val)
  gen.next().value.then(val => {
    console.log(val)
    gen.next().value.then(val => {
      console.log(val)
    })
  })
})

//輸出1 2 3

這里面發(fā)現(xiàn),要實現(xiàn)手動執(zhí)行就得使用尾調(diào)用的方式嵌套調(diào)用then方法;所以我們要實現(xiàn)生成器能自動往下執(zhí)行,并且yield能放回resolve的值,

注意:async/await是關鍵詞 不能重寫,下面使用函數(shù)來模擬

function run(gen) {
    var g = gen()                    
     //由于每次gen()獲取到的都是最新的迭代器,因此獲取迭代器操作要放在_next()之前,否則會進入死循環(huán)
  
    function _next(val) {            
         //封裝一個方法, 遞歸執(zhí)行g.next()
      var res = g.next(val)           
      //獲取迭代器對象,并返回resolve的值
      if(res.done) return res.value   
      //遞歸終止條件
      res.value.then(val => {        
         //Promise的then方法是實現(xiàn)自動迭代的前提
        _next(val)                   
         //等待Promise完成就自動執(zhí)行下一個next,并傳入resolve的值
      })
    }
    _next() 
     //第一次執(zhí)行
  }

在上面的代碼中 我們通過判斷條件 來實現(xiàn)循環(huán)調(diào)用,把下一步的操作封裝成——next(),每次Promise.then()的時候都執(zhí)行——next(),實現(xiàn)循環(huán)調(diào)用的效果, 這樣可以簡化上一個代碼塊如下:

function* myGenerator() {
  console.log(yield Promise.resolve(1))   //1
  console.log(yield Promise.resolve(2))   //2
  console.log(yield Promise.resolve(3))   //3
}

run(myGenerator)

返回Promise和異常處理
這兩個也是上面提到的特點;

返回promise:這里我們可以把上述代碼中的(gen().next.value)都用Promise.resolve()轉(zhuǎn)化一遍

異常處理:如果promise執(zhí)行失敗,導致后續(xù)的執(zhí)行中斷,采用Generator.prototype.throw()來拋出錯誤,這樣能被外層的try-catch捕獲到

所以這里我們改造一下上面一段代碼:

function run(gen) {
    //把返回值包裝成promise
    return new Promise((resolve, reject) => {
        var g = gen()

        function _next(val) {
            //錯誤處理
            try {
                var res = g.next(val)
            } catch (err) {
                return reject(err);
            }
            if (res.done) {
                return resolve(res.value);
            }
            //res.value包裝為promise,以兼容yield后面跟基本類型的情況
            Promise.resolve(res.value).then(
                val => {
                    _next(val);
                },
                err => {
                    //拋出錯誤
                    g.throw(err)
                });
        }
        _next();
    });
}

測試:

function* myGenerator() {
  try {
    console.log(yield Promise.resolve(1)) 
    console.log(yield 2)   //2
    console.log(yield Promise.reject('error'))
  } catch (error) {
    console.log(error)
  }
}

const result = run(myGenerator)     //result是一個Promise
//輸出 1 2 error

寫到最后發(fā)現(xiàn),主要去講他們怎么實現(xiàn),都8000多字,尷尬,很多概念還沒講。深入學習之后發(fā)現(xiàn)每一個點都能寫很多,所以只是寫一些主要知識點,在實現(xiàn)后也能看出他們的原理,以及概念在運行過程中是怎么用的。文章是在學習過程中寫的,有很多概念可能有錯,如果有大佬能指出不勝感激!!

參考鏈接:

# 9k字 | Promise/async/Generator實現(xiàn)原理解析

# BAT前端經(jīng)典面試問題:史上最最最詳細的手寫Promise教程文章來源地址http://www.zghlxwxcb.cn/news/detail-404304.html

到了這里,關于深入學習JavaScript系列(七)——Promise async/await generator的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領支付寶紅包贊助服務器費用

相關文章

  • promise及異步編程async await

    promise及異步編程async await

    ECMAScript 6 新增了正式的 Promise(期約)引用類型,支持優(yōu)雅地定義和組織異步邏輯。接下來幾個版本增加了使用 async 和 await 定義異步函數(shù)的機制 JavaScript 是單線程事件循環(huán)模型。異步行為是為了優(yōu)化因計算量大而時間長的操作,只要你不想為等待某個異步操作而阻塞

    2024年02月04日
    瀏覽(24)
  • async/await實現(xiàn)Promise.all()

    ??個人主頁: 不叫貓先生 ???♂?作者簡介:專注于前端領域各種技術,熱衷分享,期待你的關注。 ??系列專欄:vue3從入門到精通 ??個人簽名:不破不立 Promise.all() 方法接收一個 promise 的 iterable 類型(注:Array,Map,Set 都屬于 ES6 的 iterable 類型)的輸入,并且 只返回

    2024年01月18日
    瀏覽(44)
  • 徹底理解Promise和async/await

    1.異步行為是 為了優(yōu)化因計算量大而時間長的操作 . 2.pedding 待定: 表示尚未開始或正在進行中 ? ?fulfilled 解決: 表示已經(jīng)成功完成 ? ?rejected 拒絕: 表示沒有完成 3.從pedding狀態(tài)切換到fulfilled狀態(tài)或rejected狀態(tài)后,狀態(tài)就不會再改變.而且也不能保證promise比如會脫離待定狀態(tài). 因此

    2024年02月03日
    瀏覽(27)
  • ES6 Promise/Async/Await使用

    Promise應用 在工作中, 我們經(jīng)常會遇到用異步請求數(shù)據(jù), 查詢一個結果, 然后把返回的參數(shù)放入到下一個執(zhí)行的異步函數(shù)像這樣: 當我們使用Promise后, 我們的程序就變成了這樣: 控制臺輸出如下: async/await應用 看是不是簡潔很多了, 如果你不想使用這種鏈式調(diào)用, 也可以結合async/

    2024年02月12日
    瀏覽(20)
  • 前端面試:【異步編程】Callback、Promise和Async/Await

    嗨,親愛的JavaScript探險家!在JavaScript開發(fā)的旅程中,你會經(jīng)常遇到異步編程的需求。為了處理異步操作,JavaScript提供了多種機制,包括Callbacks、Promises和Async/Await。本文將深入介紹這些機制,讓你能夠更好地處理異步任務。 1. Callbacks:傳統(tǒng)的異步方式 Callbacks是JavaScript中最早

    2024年02月11日
    瀏覽(98)
  • 前端異步編程全套:xmlhttprequest > ajax > promise > async/await

    同步與異步區(qū)別 同步:按順序,依次執(zhí)行,向服務器發(fā)送請求--客戶端做其他操作 異步:分別執(zhí)行,向服務器發(fā)送請求==同時執(zhí)行其他操作 原生xmlhttprequest 四步驟 創(chuàng)建ajax對象 設定數(shù)據(jù)的傳輸方式(get、post),打開連接open() 獲得響應數(shù)據(jù) 屬性 描述 onreadystatechange 當readysta

    2024年02月01日
    瀏覽(105)
  • 什么是Promise對象?它的狀態(tài)有哪些?如何使用Promise處理異步操作?以及 async、await

    什么是Promise對象?它的狀態(tài)有哪些?如何使用Promise處理異步操作?以及 async、await

    前端入門之旅:探索Web開發(fā)的奇妙世界 記得點擊上方或者右側(cè)鏈接訂閱本專欄哦 幾何帶你啟航前端之旅 歡迎來到前端入門之旅!這個專欄是為那些對Web開發(fā)感興趣、剛剛踏入前端領域的朋友們量身打造的。無論你是完全的新手還是有一些基礎的開發(fā)者,這里都將為你提供一

    2024年02月11日
    瀏覽(51)
  • ES6中Promise、Async/await解決回調(diào)地獄、Proxy代理

    1.Promise 作為一些場景必須要使用的一個對象,比如說我們要發(fā)送一個請求,但是在發(fā)送這個請求之前我們需要以另一個請求返回的結果中的一個數(shù)據(jù)作為這次請求的參數(shù),也就是說這個請求必須在另一個請求后面,當然我們用setTimeout定時器寫一個延時函數(shù)也可以,但是當有

    2024年02月12日
    瀏覽(23)
  • 前端常見面試題之異步(event loop, promise, async/await, 宏任務/微任務)

    前端常見面試題之異步(event loop, promise, async/await, 宏任務/微任務)

    從前到后,一行一行執(zhí)行 如果某一行執(zhí)行報錯,則停止下面代碼執(zhí)行 先把同步代碼執(zhí)行完,再執(zhí)行異步 示例: 輸出結果為: 可以看到,在執(zhí)行異步操作的過程中,主線程不會等待異步操作結束,而是繼續(xù)往下執(zhí)行后續(xù)的代碼,當滿足條件時觸發(fā)異步操作的回調(diào)函數(shù)。 異步

    2024年02月01日
    瀏覽(30)
  • vue async await promise 等待異步接口執(zhí)行完畢再進行下一步操作

    vue async await promise 等待異步接口執(zhí)行完畢再進行下一步操作

    需求:上傳多個文件,每上傳一個文件異步請求一下后臺接口,并返回一個新文件名,等把所有的異步接口執(zhí)行成功后,將上傳已成功后新文件名數(shù)組得到再去更新業(yè)務數(shù)據(jù) uni-file-picker 文件上傳組件的配置 選擇文件后,上傳到服務器后端,一個一個的傳,等異步執(zhí)行完一下

    2024年02月12日
    瀏覽(29)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領取紅包,優(yōu)惠每天領

二維碼1

領取紅包

二維碼2

領紅包