ES6版Promise实现,给你不一样的体验
ES6版Promise實現,給你不一樣的體驗
摘要: 在很久很久以前,Promise還沒有來到這個世上。那時森林里的有這樣一群攻城獅,他們飽受回調地獄(回調金字塔)的摧殘,苦不堪言。直到有一天,一位不愿意留下姓名的特工橫空出世,將他們從回調地獄中解救了出來,代號Promise。自此,很多人都踏上了尋找Promise的道路,我亦如此...
友情提醒: 本文使用ES6實現的Promise,不會的童鞋們請自行腦補!What?這位同學你竟然不知道ES6,好的,放學了請不要走,我們單獨交流一下......
回調地獄 VS Promise
就拿fs(node的核心包)來說吧,假如我們需要同時請求a.txt和b.txt中的數據,然后對數據進行操作。這種需求在我們的開發(fā)中也經常遇到哦!
- 曾經的回調地獄
- 現在的Promise
相比較之下,Promise和回調地獄的戰(zhàn)爭起初就不是一個等級的呀,回調地獄聽起來強大,但實則一點不經揍啊!Promise此時的內心應該是這樣的:
Promise之自己實現
看到這里,相信大家都很想知道Promise的核心實現是什么?接下來,請小伙伴們不要閉眼,看這里,看這里!我便說說我是如何在尋找Promise的道路上一條道走到黑的。(這標題起的,笑出豬叫聲)
1、Promise 類封裝
起初,我發(fā)現Promise是可以被new的,說明Promise 的出身是一個類啊,這可是一條很有價值的線索啊。(大家都知道,還用你說)
class Promise {constructor(executor) { // executor是new Promise的參數this.status = 'pending'; // 保存狀態(tài)this.reason = undefined; // 失敗的原因this.value = undefined; // 成功的結果let resolve = (value)=> {if(this.status === 'pending'){this.status = 'resolved';this.value = value;}}let reject = (reason)=>{if(this.status === 'pending'){this.status = 'rejected';this.reason = reason;}}try {executor(resolve, reject); // 執(zhí)行器 } catch (e) {reject(e);}}// 定義實例上的then方法,then調用的時候都是異步調用的 then(onFulfilled, onRejected) {if(this.status === 'resolved'){ // status狀態(tài)改變時執(zhí)行onFulFilledonFulfilled(this.value);}if(this.status === 'rejected'){ // status狀態(tài)改變時執(zhí)行onFulFilledonRejected(this.reason);}} } 復制代碼這怎么僅僅一條線索就寫出來這么東東呀,真讓人摸不著頭腦!別急,聽我慢慢道來:
- executor:執(zhí)行器,默認是new的時候就自動執(zhí)行,executor的執(zhí)行是同步的,為什么要try一下呢,executor執(zhí)行時如果throw new Error('error'),直接走reject
- resolve, reject:定義了executor的resolve成功的回調函數和reject失敗的回調函數兩個參數
- reason,value:分別代表了成功返回的值和失敗的原因
- status:保存了Promise的三種狀態(tài)pending(等待態(tài)),fulfilled(成功態(tài)),rejected(失敗態(tài))
- 當一個promise的狀態(tài)處于pending時,它可以過渡到fulfilled或者ejected
- 當一個promise的狀態(tài)處于fulfilled時或者rejected時,不能再過渡到其他任何狀態(tài)
- then函數: 因Promise是可以鏈式調用的,說明then函數是定義在Promise類的原型Prototype上的。
這樣我們就成功處理了同步情況下的Promise,是不是覺得自己已經追尋到Promise的終極力量了呢。(抽根煙,平復下躁動的心情)
2、Promise異步的實現
在我們平時的開發(fā)中,往往異步代碼比較多,異步執(zhí)行需要,然而Promise的executor執(zhí)行器又是同步執(zhí)行的,它不等我們怎么辦呢,好著急有木有。 我們在上面代碼的基礎上新增如下幾行代碼:
class Promise {constructor(executor) {this.onResolvedCallbacks = []; // 保存成功的回調this.onRejectedCallbacks = []; // 保存失敗的回調let resolve = (value)=> {if(this.status === 'pending'){this.status = 'resolved';this.value = value;this.onResolvedCallbacks.forEach(fn=>fn());}}let reject = (reason)=>{if(this.status === 'pending'){this.status = 'rejected';this.reason = reason;this.onRejectedCallbacks.forEach(fn=>fn());}}}then(onFulfilled, onRejected) { if(this.status === 'pending'){this.onResolvedCallbacks.push(()=>{onFulfilled(this.value);});this.onRejectedCallbacks.push(()=>{onRejected(this.reason);});});}} } 復制代碼當出現異步代碼時,status的狀態(tài)還是pending,我們可以先把then函數中成功和失敗的回調保存下來,等到異步代碼執(zhí)行完成后,status的狀態(tài)改變了,我們再去依次執(zhí)行保存下來的回調函數。
看到這里,如果覺得自己已經基本掌握Promise的實現,只能說爾等對Promise的核心力量一無所知。(別廢話,趕緊寫)好的,各位大佬!
3、Promise之鏈式調用的實現
在開始實現之前呢,我們先來看一下如下代碼:
// 這里的Promise是ES6封裝好的,并不是我們自己實現的 let promise = new Promise((resolve,reject)=>{ resolve('123'); }) let promise2 = promise.then((data)=>{throw new Error('error'); }) promise2.then((data)=>{console.log(data); },(err)=>{console.log(err); // 這里輸出了error }) 復制代碼上面代碼說明then函數執(zhí)行后返回的promise2實例并不是promise實例,說明返回值不是this,因為promise不能即調用成功后不能再走失敗,所以then函數執(zhí)行后返回的promise2是一個新的promise實例。(跟jQuery的鏈式調用不一樣哦)
Promise的constructor的代碼不需要改變,只需要對then函數進行再次封裝:
then(onFulfilled, onRejected) {// onFulfilled和onRejected可能沒傳onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value=>value;onRejected = typeof onRejected === 'function' ? onRejected : (err)=>{throw err};let promise2; // 需要每次調用then方法時,都需要返回新的promisepromise2 = new Promise((resolve, reject)=>{if(this.status === 'resolved'){setTimeout(()=>{try {let x = onFulfilled(this.value); // 執(zhí)行完當前回調后返回的結果可能還是個promiseresolvePromise(promise2,x,resolve, reject);} catch (e) {reject(e);}},0)}if(this.status === 'rejected'){setTimeout(()=>{try {let x = onRejected(this.reason);resolvePromise(promise2,x,resolve, reject);} catch (e) {reject(e);}},0) }if(this.status === 'pending'){this.onResolvedCallbacks.push(()=>{setTimeout(()=>{try {let x = onFulfilled(this.value);resolvePromise(promise2,x,resolve, reject);} catch (e) {reject(e);}},0)});this.onRejectedCallbacks.push(()=>{setTimeout(()=>{try {let x = onRejected(this.reason);resolvePromise(promise2,x,resolve, reject);} catch (e) {reject(e);}},0)});}})return promise2;} 復制代碼- onFulfilled,onRejected:當沒有傳的時候,需要做的處理
- promise2:then函數的返回值是一個新的promise
- setTimeout:Promise/A+規(guī)范(規(guī)范)要求then函數必須是異步的,當然原生的Promise實現并不是用的setTimeout,而是一個微任務
- resolvePromise:封裝resolvePromise方法,當then函數中的成功或者失敗函數返回值x可能還是個promise
定義的resolvePromise方法:
let resolvePromise = (promise2,x,resolve, reject)=>{let called;// promise2和函數的返回值是同一個if(promise2 === x){return reject(new TypeError('Chaining cycle detected for promise #<Promise>'));}if(x!==null && (typeof x === 'object' || typeof x === 'function')){try {let then = x.then;if(typeof then === 'function'){then.call(x,(y)=>{if(called) return;called = true;resolvePromise(promise2,y,resolve, reject);// 遞歸處理,直到y是一個普通值},(err)=>{if(called) return;called = true;reject(err);})}else{ // then如果是一個常量if(called) return;called = true;resolve(x);}} catch (e) {if(called) return;called = true;reject(e);}}else{ // x如果是一個常量if(called) return;called = true;resolve(x);} } 復制代碼- 四個參數:promise2 (then函數的返回值,是一個新的promise) x(then中成功后者失敗函數的返回值)resolve(promise2的resolve)reject(promise2的reject)
- called: 加了called判斷,防止多次調用,因為這里的邏輯不單單是自己的,還有別人的,別人的promise可能即會調用成功也會調用失敗
- let then = x.then:x可能還是一個promise,那么就讓這個Promise執(zhí)行
至此,我們終于追尋到了promise的核心力量所在。來,讓我們小小的慶賀一下:
3、Promise之類上的方法實現
當然,我們已經初步了解了promise的核心力量,在我們開發(fā)的過程中,除了then方法,也會使用它的一些其他常用的方法,就像一位身經百戰(zhàn)的特工,你除了會用刀,還要會用槍不是。我們在Promise類上定義它們:
static resolve(value){return new Promise((resolve,reject)=>{resolve(value);})}static reject(reason){return new Promise((resolve,reject)=>{reject(reason);})}static all(promises){return new Promise((resolve,reject)=>{let arr = [];let i = 0;let processData = (index,data)=>{arr[index] = data;if(++i === promises.length){resolve(arr);}}for(let i = 0; i< promises.length; i++){promises[i].then(data=>{processData(i,data);},reject);}})}static race(promises){return new Promise((resolve,reject)=>{for(let i = 0; i< promises.length; i++){promises[i].then(resolve,reject);}})}catch(onRejected){return this.then(null,onRejected);} 復制代碼相信resolve,reject,all,race這四個類上的方法和catch這個原型的方法大家都已經很熟悉了,我就不過多的啰嗦了。
因為,我實在是編不下去了,我還有更重要的事情要去做:
結語: 花了很久寫的這篇文章,如果這篇文章令你或多或少有些收獲,請不要吝嗇你的贊美(點個贊再走嗎,小哥哥小姐姐),如果有寫的不對的地方,也希望各位大佬能不吝指教,萬分感謝!原創(chuàng)文章,轉載請注明出處!
總結
以上是生活随笔為你收集整理的ES6版Promise实现,给你不一样的体验的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JavaScript对象——原型与原型链
- 下一篇: 暑假周报告(第三周)