摘要:本篇文章將會嘗試用簡單易懂的語言描述的原理,并且用手擼一個簡單的。一個后可以通過方法,指定和時的回調函數。實現實現狀態機因為是一個構造函數,使用的寫法,首先想到的就是有顯式聲明的。
說到Promise,都知道它是比回調函數更優的一種異步編程解決方案,它可以使得異步操作邏輯變得更加清晰,是解決地獄回調的一種嘗試。本篇文章將會嘗試用簡單易懂的語言描述Promise的原理,并且用es6手擼一個簡單的Promise。1. Promise的原理
原理:promise對象有三種狀態,pending、fulfilled和rejected。promise對象內部保存一個需要執行一段時間的異步操作,當異步操作執行結束后可以調用resolve或reject方法,來改變promise對象的狀態,狀態一旦改變就不能再變。new一個promise后可以通過then方法,指定resolved和rejected時的回調函數。下面是我們日常使用Promise的代碼邏輯。
let getAsyncData = new Promise((resolve, reject) => { // 執行一些異步操作 if (// 如果成功) { // ...執行代碼 resolve(); } else { // 如果失敗 // ...執行代碼 reject(); } }) ); getAsyncData.then(success, fail).then(success, fail)
結合Promise A+規范,我們就可以分析一下我們要實現一個什么東西:
實現一個狀態機,有三個狀態,pending、fulfilled、rejected,狀態之間的轉化只能是pending->fulfilled、pending->rejected,狀態變化不可逆。
實現一個then方法,可以用來設置成功和失敗的回調
then方法要能被調用多次,所以then方法需要每次返回一個新的promise對象,這樣才能支持鏈式調用。
構造函數內部要有一個value值,用來保存上次執行的結果值,如果報錯,則保存的是異常信息。
那我們現在就按照上面提到的原理和規范來實現這個Promise構造函數。
2. 實現 2.1 實現狀態機// promise.js class Promise { constructor (executor) { this.status = PENDING; this.value = ""; executor(this.resolve, this.reject); } resolve (value) { if (this.status === PENDING) { this.value = value; this.status = FULFILLED; } } reject (value) { if (this.status === PENDING) { this.value = value; this.status = REJECTED; } } then (onfulfilled, onrejected) { if (this.status === FULFILLED) { onfulfilled(this.value); } if (this.status === REJECTED) { onrejected(this.value); } } } const PENDING = "pending"; const FULFILLED = "fulfilled"; const REJECTED = "rejected"; const test = new Promise((resolve, reject) => { resolve(100); }); test.then((data) => { console.log(data); },(data) => {});
因為Promise是一個構造函數,使用ES6的寫法,首先想到的就是有顯式constructor聲明的class。上面就是我們用class的實現,可以看到這樣我們就實現了這個狀態機,有status, value兩個屬性和resolve, reject, then三個函數;同時它有pending, fulfilled和rejected三個狀態,其中pending就可以切換為fulfilled或者rejected兩種。
看來起還行的樣子,嘗試著運行一下,報錯了。
ReferenceError: resolve is not defined
這是因為在class中使用this要格外小心,類的方法內部如果含有this,它默認指向類的實例,而如果多帶帶使用這個方法(上面代碼中的resolve(100)),this就會指向該方法運行時所在的環境,從而因為找不到這個方法而報錯。所以,要在構造函數中綁定this。constructor改為
constructor (executor) { this.status = PENDING; this.value = ""; this.resolve = this.resolve.bind(this); this.reject = this.reject.bind(this); this.then = this.then.bind(this); executor(this.resolve, this.reject); }
再運行一下,輸出了100,但是現在其實不是一個異步處理方案,代碼先運行了resolve(100)然后又運行了then函數,其實對于異步的情況沒有處理,不信的話就給resolve加一個setTimeout,好了,代碼又沒有輸出了。
2.2 實現異步設置狀態作為一個異步處理的函數,在使用的時候,我們肯定是會先設置好不同異步返回后的處理邏輯(即then的成功、失敗調用函數),然后安心等待異步執行,最后再異步結束后,系統會自動根據我們的邏輯選擇調用不同回調函數。換句話說,then函數要對status為pending的狀態進行處理。處理的原理是設置兩個數組,在pending狀態下分別保存成功和失敗回調函數,當狀態改變后,再根據狀態去調用數組中保存的回調函數。
我們將代碼改變如下:
class Promise { constructor (executor) { this.status = PENDING; this.value = ""; this.onfulfilledArr = []; this.onrejectedArr = []; this.resolve = this.resolve.bind(this); this.reject = this.reject.bind(this); this.then = this.then.bind(this); executor(this.resolve, this.reject); } resolve (value) { if (this.status === PENDING) { this.value = value; this.onfulfilledArr.forEach(item => { item(this.value); }) this.status = FULFILLED; } } reject (value) { if (this.status === PENDING) { this.value = value; this.onrejectedArr.forEach(item => { item(this.value); }) this.status = REJECTED; } } then (onfulfilled, onrejected) { if (this.status === FULFILLED) { onfulfilled(this.value); } if (this.status === REJECTED) { onrejected(this.value); } if (this.status === PENDING) { this.onfulfilledArr.push(onfulfilled); this.onrejectedArr.push(onrejected); } } } const PENDING = "pending"; const FULFILLED = "fulfilled"; const REJECTED = "rejected"; const test = new Promise((resolve, reject) => { setTimeout(() => { resolve(100); }, 2000) }); test.then((data) => { console.log(data); },(data) => {});
再運行一下,ok正常輸出了。但是Promise的一大特點就是可以鏈式調用,即test.then(success, fail).then(success, fail)...這就需要then返回一個新的Promise對象,而我們的程序現在明顯的是不支持的。那么繼續改一下。
2.3 實現鏈式調用再觀察一下鏈式調用,如果成功和失敗的函數中有返回值,這個值要作為參數傳給下個then函數的成功或失敗回調。所以我們要在返回的new Promise中調用相應的函數。
class Promise { constructor (executor) { this.status = PENDING; this.value = ""; this.onfulfilledArr = []; this.onrejectedArr = []; this.resolve = this.resolve.bind(this); this.reject = this.reject.bind(this); this.then = this.then.bind(this); executor(this.resolve, this.reject); } resolve (value) { if (this.status === PENDING) { this.value = value; this.onfulfilledArr.forEach(item => { item(this.value); }) this.status = FULFILLED; } } reject (value) { if (this.status === PENDING) { this.value = value; this.onrejectedArr.forEach(item => { item(this.value); }) this.status = REJECTED; } } then (onfulfilled, onrejected) { if (this.status === FULFILLED) { const res = onfulfilled(this.value); return new Promise(function(resolve, reject) { resolve(res); }) } if (this.status === REJECTED) { const res = onrejected(this.value); return new Promise(function(resolve, reject) { reject(res); }) } if (this.status === PENDING) { const self = this; return new Promise(function(resolve, reject) { self.onfulfilledArr.push(() => { const res = onfulfilled(self.value) resolve(res); }); self.onrejectedArr.push(() => { const res = onrejected(self.value) reject(res); }); }) } } } const PENDING = "pending"; const FULFILLED = "fulfilled"; const REJECTED = "rejected"; const test = new Promise((resolve, reject) => { setTimeout(() => { resolve(100); }, 2000) }); test.then((data) => { console.log(data); return data + 5; },(data) => {}) .then((data) => { console.log(data) },(data) => {});
再運行一下,輸出100,105。好了,一個簡單的Promise就實現好了。
3. 總結Promise其實就是對異步操作的一種封裝方式,可以使得回調的流程變得清晰一些,但是本質上并不解決回調地獄。因為如果有多個異步操作嵌套,then也要一直寫下去。所以后來ES6又有了Generator,允許我們用同步的方式來寫異步的代碼,以及它的語法糖async/await,當然這就是后話了。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/96847.html
摘要:介紹簡單點來說就就是一個配置文件,所有的魔力都是在這一個文件中發生的。一安裝全局安裝在文件夾里面也需要安裝這個是在你根目錄下進行的全局安裝記得加太慢,推薦用的鏡像安裝方法一樣。二創建項目新建文件夾命令行輸入命令。 介紹:webpack簡單點來說就就是一個配置文件,所有的魔力都是在這一個文件中發生的。 這個配置文件主要分為三大塊 entry 入口文件 讓webpack用哪個文件作為項目的...
摘要:更好的異步編程上面的方法可以適用于那些比較簡單的異步工作流程。小結的組合目前是最強大,也是最優雅的異步流程管理編程方式。 訪問原文地址 generators主要作用就是提供了一種,單線程的,很像同步方法的編程風格,方便你把異步實現的那些細節藏在別處。這讓我們可以用一種很自然的方式書寫我們代碼中的流程和狀態邏輯,不再需要去遵循那些奇怪的異步編程風格。 換句話說,通過將我們generato...
摘要:手寫一款符合規范的長篇預警有點長,可以選擇性觀看。初始狀態是,狀態可以有或者不能從轉換為或者從轉換成即只要由狀態轉換為其他狀態后,狀態就不可變更。 手寫一款符合Promise/A+規范的Promise 長篇預警!有點長,可以選擇性觀看。如果對Promise源碼不是很清楚,還是推薦從頭看,相信你認真從頭看到尾,并且去實際操作了,肯定會有收獲的。主要是代碼部分有點多,不過好多都是重復的,不...
摘要:圖片壓縮的原理大同小異,這里直接引用官方文檔的原話基本原理是通過渲染圖片,再通過方法壓縮保存為字符串能夠編譯為格式的圖片。這個過程我自己手擼過,代碼很多,更不用提有各種的兼容性坑,所以最后權衡再三還是直接換成了這個插件。 慣例,先貼傳送門:https://github.com/think2011/localResizeIMG 首先說到,為嘛要壓縮圖片,這需求一般出現在需要上傳照片(尤其...
閱讀 3876·2021-07-28 18:10
閱讀 2583·2019-08-30 15:44
閱讀 1094·2019-08-30 14:07
閱讀 3466·2019-08-29 17:20
閱讀 1583·2019-08-26 18:35
閱讀 3542·2019-08-26 13:42
閱讀 1822·2019-08-26 11:58
閱讀 1594·2019-08-23 18:33