第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

全部開發(fā)者教程

ES6-10 入門教程

首頁 慕課教程 ES6-10 入門教程 ES6-10 入門教程 ES6+ 實現(xiàn)一個簡版的 Promise

ES6+ 實現(xiàn)一個簡版的 Promise

1. 前言

上一節(jié)我們學(xué)習(xí)了 ES6 Promise的基本用法,并且我們知道 Promise 最早出現(xiàn)在社區(qū),所以ES6 中 Promise 也是遵循一個標(biāo)準(zhǔn)的規(guī)范的。這個規(guī)范就是 Promise A+ 規(guī)范 也就是任何人都可以遵循這個規(guī)范實現(xiàn)一個自己的 Promise,由于每個人實現(xiàn)的方式有所差異,Promise A+ 規(guī)范 給出了一些要求和兼容方式。

本節(jié)我們將根據(jù) Promise A+ 規(guī)范 實現(xiàn)一個簡版的 Promise API。

2. 實現(xiàn)步驟

上一節(jié)我們已經(jīng)知道了 Promise 是一個類,默認(rèn)接收一個參數(shù) executor(執(zhí)行器),并且會立即執(zhí)行。所以首先需要創(chuàng)建一個 Promise 的類,然后傳入一個回調(diào)函數(shù)并執(zhí)行它,故有如下的初始代碼:

class Promise {
  constructor(executor) {
    executor();
  }
}

Promise 有三個狀態(tài):等待(padding)、成功(fulfilled),失?。╮ejected)。默認(rèn)是等待狀態(tài),等待態(tài)可以突變?yōu)槌晒B(tài)或失敗態(tài),所以我們可以定義三個常量來存放這三個狀態(tài)

const PENDING = 'PENDING';
const RESOLVED = 'RESOLVED';	// 成功態(tài)
const REJECTED = 'REJECTED';	// 失敗態(tài)
class Promise {
  constructor(executor) {
    this.status = PENDING;	// 默認(rèn)是等待態(tài)
    executor();
  }
}

這樣我們就知道了 Promise 的基本狀態(tài),那內(nèi)部的狀態(tài)是怎么突變?yōu)槌晒蚴〉哪??這里執(zhí)行器(executor)會提供兩個個方法用于改變 Promise 的狀態(tài),所以我們需要在初始化時定義 resolve 和 reject 方法:在成功的時候會傳入成功的值,在失敗的時候會傳入失敗的原因。并且每個Promise 都會提供 then方法用于鏈?zhǔn)秸{(diào)用。

class Promise {
  constructor(executor) {
    this.status = PENDING;
    const resolve = (value) => {};
    const reject = (reason) => {};
    // 執(zhí)行executor時,傳入成功或失敗的回調(diào)
    executor(resolve, reject);
  }
  then(onfulfilled, onrejected) {
    
  }
}

這時我們就可以開始著手去更改 Promise的狀態(tài)了,由于默認(rèn)情況下 Promise 的狀態(tài)只能從 pending 到 fulfilled 和 rejected 的轉(zhuǎn)化。

class Promise {
  constructor(executor) {
    this.status = PENDING;
    const resolve = (value) => {
      // 只有等待態(tài)時才能更改狀態(tài)
      if (this.status === PENDING) {
        this.status = RESOLVED;
      }
    };
    const reject = (reason) => {
      if (this.status === PENDING) {
        this.status = REJECTED;
      }
    };
    executor(resolve, reject);
  }
  ...
}

成功和失敗都會返回對應(yīng)的結(jié)果,所以我們需要定義成功的值和失敗的原因兩個全局變量,用于存放返回的結(jié)果。

class Promise {
  constructor(executor) {
    this.status = PENDING;
    this.value = undefined;
    this.reason = undefined;
    const resolve = (value) => {
      // 只有等待態(tài)時才能更改狀態(tài)
      if (this.status === PENDING) {
        this.value = value;
        this.status = RESOLVED;
      }
    };
    const reject = (reason) => {
      if (this.status === PENDING) {
        this.reason = reason;
        this.status = REJECTED;
      }
    };
    executor(resolve, reject);
  }
  ...
}

這時我們就已經(jīng)為執(zhí)行器提供了兩個回調(diào)函數(shù)了,如果在執(zhí)行器執(zhí)行時拋出異常時,我們需要使用 try…catch 來補(bǔ)貨一下。由于是拋出異常,所以,需要調(diào)用 reject 方法來修改為失敗的狀態(tài)。

try {
  executor(resolve, reject);
} catch(e) {
  reject(e)
}

我們知道實例在調(diào)用 then 方法時會傳入兩個回調(diào)函數(shù) onfulfilled, onrejected 去執(zhí)行成功或失敗的回調(diào),所以根據(jù)狀態(tài)會調(diào)用對應(yīng)的函數(shù)來處理。

then(onfulfilled, onrejected) {
  if (this.status === RESOLVED) {
    onfulfilled(this.value)
  }
  if (this.status === REJECTED) {
    onrejected(this.reason)
  }
}

這樣我們就完了 Promise 最基本的同步功能,

let promise = new Promise((resolve, reject) => {
  resolve('value');
  // throw new Error('錯誤');
  // reject('error reason')
  // setTimeout(() => {
  //   resolve('value');
  // }, 1000)
})
promise.then((data) => {
  console.log('resolve response', data);
}, (err) => {
  console.log('reject response', err);
})

用上面的代碼對我們寫的 Promise 進(jìn)行驗證,通過測試用例可知,我們寫的 Promise 只能在同步中運(yùn)行,當(dāng)我們使用 setTimeout 異步去返回時,并沒有預(yù)想的在then的成功回調(diào)中打印結(jié)果。

對于這種異步行為需要專門處理,如何處理異步的內(nèi)容呢?我們知道在執(zhí)行異步任務(wù)時 Promise 的狀態(tài)并沒有被改變,也就是并沒有執(zhí)行 resolve 或 reject 方法,但是 then 中的回調(diào)已經(jīng)執(zhí)行了,這時就需要增加當(dāng) Promise 還是等待態(tài)的邏輯,在等待態(tài)時把回調(diào)函數(shù)都存放起來,等到執(zhí)行 resolve 或 reject 再依次執(zhí)行之前存放的then的回調(diào)函數(shù),也就是我們平時用到的發(fā)布訂閱模式。實現(xiàn)步驟:

  • 首先,需要在初始化中增加存放成功的回調(diào)函數(shù)和存放失敗的回調(diào)函數(shù);
  • 然后,由于是異步執(zhí)行 resolve 或 reject 所以需要在 then 方法中把回調(diào)函數(shù)存放起來;
  • 最后,當(dāng)執(zhí)行 resolve 或 reject 時取出存放的回調(diào)函數(shù)依次執(zhí)行。

根據(jù)以上的實現(xiàn)步驟可以得到如下的代碼:

class Promise {
  constructor(executor) {
	this.status = PENDING;
    this.value = undefined; // 成功的值
    this.reason = undefined; // 失敗的原因
+   // 存放成功的回調(diào)函數(shù)
+    this.onResolvedCallbacks = [];
+    // 存放失敗的回調(diào)函數(shù)
+    this.onRejectedCallbacks = [];
    let resolve = (value) => {
      if (this.status === PENDING) {
		this.value = value;
        this.status = RESOLVED;
+       // 異步時,存放在成功的回調(diào)函數(shù)依次執(zhí)行
+       this.onResolvedCallbacks.forEach(fn => fn())
      }
    };
    let reject = (reason) => {
      if (this.status === PENDING) {
		this.value = reason;
        this.status = REJECTED;
+       // 異步時,存放在失敗的回調(diào)函數(shù)依次執(zhí)行
+       this.onRejectedCallbacks.forEach(fn => fn())
      }
    };
    try {
      executor(resolve, reject);
    } catch(e) {
      reject(e)
    }
  }
  then(onfulfilled, onrejected) {
    if (this.status === RESOLVED) {
      onfulfilled(this.value)
    }
    if (this.status === REJECTED) {
      onrejected(this.reason)
    }
+    if (this.status === PENDING) {
+      this.onResolvedCallbacks.push(() => {
+        // TODO
+        onfulfilled(this.value);
+      })
+      this.onRejectedCallbacks.push(() => {
+        // TODO
+        onrejected(this.reason);
+      })
+    }
  }
}

上面的代碼中,在存放回調(diào)函數(shù)時把 onfulfilled, onrejected 存放在一個函數(shù)中執(zhí)行,這樣的好處是可以在前面增加處理問題的邏輯。這樣我們就完成了處理異步的 Promise 邏輯。下面是測試用例,可以正常的執(zhí)行 then 的成功回調(diào)函數(shù)。

let promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('100');
  }, 1000)
})
promise.then((data) => {
  console.log('resolve response:', data); // resolve response: 100
}, (err) => {
  console.log('reject response:', err);
})

到這里我們是不是已經(jīng)基本實現(xiàn)了 Promise 的功能呢?ES6 中的 then 方法支持鏈?zhǔn)秸{(diào)用,那我們寫的可以嗎?我們在看下面的一個測試用例:

let promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('100');
  }, 1000)
})
promise.then((data) => {
  console.log('resolve response:', data); // resolve response: 100
  return 200
}, (err) => {
  console.log('reject response:', err);
}).then((data) => {
  console.log('data2:', data)
}, null)
// TypeError: Cannot read property 'then' of undefined

然而當(dāng)我們在執(zhí)行的時候會報錯,then 是 undefined。為什么會這樣呢?那我們要知道如何滿足鏈?zhǔn)秸{(diào)用的規(guī)范,那就是在完成任務(wù)后再返回一個Promise 實例。那如何返回一個 Promise 實例呢?在 Promise A+ 規(guī)范的 2.2.7 小節(jié)在有詳細(xì)的描述,再實例化一個 promise2 來存放執(zhí)行后的結(jié)果,并返回 promise2。那么我們就要改造 then 方法了。

class Promise {
  ...
  then(onfulfilled, onrejected) {
	let promise2 = new Promise((resolve, reject) => {
      if (this.status === RESOLVED) {
        const x = onfulfilled(this.value)
		resolve(x)
      }
      if (this.status === REJECTED) {
        const x = onrejected(this.reason);
        reject(x)
      }
      if (this.status === PENDING) {
        this.onResolvedCallbacks.push(() => {
	        const x = onfulfilled(this.value)
			resolve(x)
        })
        this.onRejectedCallbacks.push(() => {
	        const x = onrejected(this.reason);
        	reject(x)
        })
      }
    })

    return promise2
  }
}

再使用上面的測試用例,就可以得到正確的結(jié)果:

let promise = new Promise((resolve, reject) => {
  resolve('100');
})
promise.then((data) => {
  console.log('data1:', data);	// data1: 100
  return 200
}, null).then((data) => {
  console.log('data2:', data);	// data2: 200
  throw new Error('error')
}, null).then(null, () => {
  consol.log('程序報錯...')
})

上面的測試用例中,當(dāng) then 的回調(diào)函數(shù)拋出異常時需要去捕獲錯誤,傳到下一個 then 的失敗回調(diào)函數(shù)中。

class Promise {
  ...
  then(onfulfilled, onrejected) {
		let promise2 = new Promise((resolve, reject) => {
      if (this.status === RESOLVED) {
        try{
			const x = onfulfilled(this.value)
			resolve(x)
        } catch(e) {
			reject(e)
        }
      }
      if (this.status === REJECTED) {
        try{
			const x = onrejected(this.reason);
        	resolve(x)
        } catch(e) {
			reject(e)
        }
      }
      if (this.status === PENDING) {
        this.onResolvedCallbacks.push(() => {
          try{
            const x = onfulfilled(this.value)
            resolve(x)
          } catch(e) {
            reject(e)
          }
        })
        this.onRejectedCallbacks.push(() => {
          try{
            const x = onrejected(this.reason);
            resolve(x)
          } catch(e) {
            reject(e)
          }
        })
      }
    })
    return promise2
  }
}

到這里為止我們就已經(jīng)實現(xiàn)了一個簡版的 Promise,因為Promise是一個規(guī)范,很多人都可以實現(xiàn)自己的 Promise 所以 Promise A+ 規(guī)范做了很多兼容處理的要求,如果想實現(xiàn)一個完整的 Promise 可以參考 Promise A+ 規(guī)范

3. 小結(jié)

本節(jié)主要按照 Promise A+ 規(guī)范 部分的要求實現(xiàn)了一個簡版的 Promise API 這個 Promise 基本上滿足同步異步的鏈?zhǔn)秸{(diào)用,對基本的異常做了處理。當(dāng)然 Promise A+ 規(guī)范 所規(guī)定的細(xì)節(jié)比較多,剩下的都是對各種異常錯誤的處理,所以后面我們也沒有去實現(xiàn)。另外官網(wǎng)下提供了一個測試用例來驗證我們寫的 Promise 是否符合 Promise A+ 規(guī)范 ,所以可以參考 promises-tests 這個庫來完成我們的 Promise 的測試。