這次帶給大家使用Js實作Promise函式庫,使用Js實作Promise函式庫的注意事項有哪些,以下就是實戰案例,一起來看一下。
前言
ECMAScript 是 JavaScript 語言的國際標準,JavaScript 是 ECMAScript 的實作。 ES6 的目標,是使得 JavaScript 語言可以用來編寫大型的複雜的應用程序,成為企業級開發語言。
概念
ES6 原生提供了 Promise 物件。
所謂 Promise,就是一個對象,用來傳遞非同步操作的訊息。它代表了某個未來才會知道結果的事件(通常是一個非同步操作),而這個事件提供統一的 API,可供進一步處理。
三道思考題
剛開始寫前端的時候,處理非同步請求常用callback,簡單又順手。後來寫著寫著就拋棄了callback,開始用promise來處理非同步問題。 promise寫起來確實更加優美,但由於缺乏對它內部結構的深刻認識,每次在遇到一些複雜的情況時,promise用起來總是不那麼得心應手,debug也得搞半天。
所以,這篇文章我會帶大家從零開始,手寫一個基本上能用的promise。跟著我寫下來以後,你會對promise是什麼以及它的內部結構有一個清楚的認知,未來在復雜場景下使用promise也能如魚得水。
而且,為了檢驗大家是否真的完全掌握了promise,我會在文章結尾出幾道跟promise相關的練習題。說是練習題,其實都是大家專案中會遇到的真實場景的抽象,熟練可以幫助大家在前端方面更上一層樓。
提前將三道練習題給出來,大家可以先不看下文的內容,在腦海裡大概構思下你會怎麼解決:
promise array的鍊式呼叫?
promise怎麼做並發控制?
promise怎麼做非同步快取?
以上三道思考題其實跟你用不用promise並沒有太大關係,但是如果你不深刻理解promise想要解決這三個問題還真不是那麼輕鬆的。
什麼是Promise
回到正文,什麼是Promise?說穿了,promise就是一個容器,裡面保存著某個未來才會結束的事件(通常是一個非同步操作)的結果。
首先,ES6規定Promise物件是一個建構函數,用來產生Promise實例。然後,這個建構子接受一個函式(executor)作為參數,該函式的兩個參數分別是resolve和reject。最後,Promise實例產生以後,可以用then方法分別指定resolved狀態和rejected狀態的回呼函數(onFulfilled和onRejected)。
具體的使用方法,用程式碼表現是這樣:
const promise = new Promise(function(resolve, reject) { // ... some code if (/* 异步操作成功 */){ resolve(value); } else { reject(error); } }); promise.then(function(value) { // success }, function(error) { // failure });
理解了這個後,我們就可以大膽的開始構造我們自己的promise了,我們給它取個名字:CutePromise
實作一個Promise:CutePromise
#我們直接用ES6的class來建立我們的CutePromise,對ES6語法還不熟悉的,可以先讀我的另外兩篇介紹ES6核心語法的文章再回來。 30分鐘掌握ES6/ES2015核心內容(上) 、30分鐘掌握ES6/ES2015核心內容(下)
class CutePromise { // executor是我们实例化CutePromise时传入的参数函数,它接受两个参数,分别是resolve和reject。 // resolve和reject我们将会定义在constructor当中,供executor在执行的时候调用 constructor(executor) { const resolve = () => {} const reject = () => {} executor(resolve, reject) } // 为实例提供一个then的方法,接收两个参数函数, // 第一个参数函数必传,它会在promise已成功(fulfilled)以后被调用 // 第二个参数非必传,它会在promise已失败(rejected)以后被调用 then(onFulfilled, onRejected) {} }
創建了我們的CutePromise後,我們再來搞清楚一個關鍵點:Promise 物件的狀態。
Promise 物件透過自身的狀態,來控制非同步操作。一個Promise 實例有三種狀態:
非同步操作未完成(pending)
非同步操作成功(fulfilled)
非同步操作失敗(rejected)
上面三種狀態裡面,fulfilled和rejected合在一起稱為resolved(已定型)。狀態的切換只有兩條路徑:第一種是從pending=>fulfilled,另一種是從pending=>rejected,狀態一旦切換就無法再改變。
現在我們來為CutePromise新增狀態,大概流程就是:
首先,实例化初始过程中,我们先将状态设为PENDING,然后当executor执行resolve的时候,将状态更改为FULFILLED,当executor执行reject的时候将状态更改为REJECTED。同时更新实例的value。
constructor(executor) { ... this.state = 'PENDING'; ... const resolve = (result) => { this.state = 'FULFILLED'; this.value = result; } const reject = (error) => { this.state = 'REJECTED'; this.value = error; } ... }
再来看下我们的then函数。then函数的两个参数,onFulfilled表示当promise异步操作成功时调用的函数,onRejected表示当promise异步操作失败时调用的函数。假如我们调用then的时候,promise已经执行完成了(当任务是个同步任务时),我们可以直接根据实例的状态来执行相应的函数。假如promise的状态还是PENDING, 那我们就将onFulfilled和onRejected直接存储到chained这个变量当中,等promise执行完再调用。
constructor(executor) { ... this.state = 'PENDING'; // chained用来储存promise执行完成以后,需要被依次调用的一系列函数 this.chained = []; const resolve = (result) => { this.state = 'FULFILLED'; this.value = result; // promise已经执行成功了,可以依次调用.then()函数里的onFulfilled函数了 for (const { onFulfilled } of this.chained) { onFulfilled(res); } } ... } then(onFulfilled, onRejected) { if (this.state === 'FULFILLED') { onFulfilled(this.value); } else if (this.state === 'REJECTED') { onRejected(this.value); } else { this.$chained.push({ onFulfilled, onRejected }); } }
这样我们就完成了一个CutePromise的创建,下面是完整代码,大家可以复制代码到控制台测试一下:
class CutePromise { constructor(executor) { if (typeof executor !== 'function') { throw new Error('Executor must be a function'); } this.state = 'PENDING'; this.chained = []; const resolve = res => { if (this.state !== 'PENDING') { return; } this.state = 'FULFILLED'; this.internalValue = res; for (const { onFulfilled } of this.chained) { onFulfilled(res); } }; const reject = err => { if (this.state !== 'PENDING') { return; } this.state = 'REJECTED'; this.internalValue = err; for (const { onRejected } of this.chained) { onRejected(err); } }; try { executor(resolve, reject); } catch (err) { reject(err); } } then(onFulfilled, onRejected) { if (this.state === 'FULFILLED') { onFulfilled(this.internalValue); } else if (this.$state === 'REJECTED') { onRejected(this.internalValue); } else { this.chained.push({ onFulfilled, onRejected }); } } }
提供一下测试代码:
let p = new CutePromise(resolve => { setTimeout(() => resolve('Hello'), 100); }); p.then(res => console.log(res)); p = new CutePromise((resolve, reject) => { setTimeout(() => reject(new Error('woops')), 100); }); p.then(() => {}, err => console.log('Async error:', err.stack)); p = new CutePromise(() => { throw new Error('woops'); }); p.then(() => {}, err => console.log('Sync error:', err.stack));
相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!
推荐阅读:
以上是使用Js實作Promise函式庫的詳細內容。更多資訊請關注PHP中文網其他相關文章!