摘要:前言在寫這個之前,希望你已經(jīng)對中的很熟悉了,概念性和基礎(chǔ)的東西就不再講了,不懂的同學(xué)可以去看看阮一峰老師的教程我主要按以下個步驟來一步一步實現(xiàn),異步的實現(xiàn)我放在了后面,所以前面幾步暫不考慮實現(xiàn)一個基本的實現(xiàn)的鏈?zhǔn)秸{(diào)用處理函數(shù)的參數(shù)是實例的情
前言
在寫這個promise之前,希望你已經(jīng)對es6中的Promise很熟悉了,概念性和基礎(chǔ)的東西就不再講了,不懂的同學(xué)可以去看看阮一峰老師的es6教程. 我主要按以下5個步驟來一步一步實現(xiàn),異步的實現(xiàn)我放在了后面,所以前面幾步暫不考慮
實現(xiàn)一個基本的MyPromise
實現(xiàn)then的鏈?zhǔn)秸{(diào)用
處理reolve函數(shù)的參數(shù)是MyPromise實例的情況以及處理then方法中前一個回調(diào)函數(shù)返回的也是一個MyPromise實例的情況
實現(xiàn)異步的MyPromise
完善MyPromise的其它方法
1.實現(xiàn)一個基本的MyPromise/* * 這里我將promise的3個狀態(tài)分別定義為: pending, resolved, rejected * 其中fn必須是個函數(shù), 必須通過new來使用 */ function MyPromise(fn) { if (!(this instanceof MyPromise)) { throw new TypeError("MyPromise must be constructed via new"); } if (typeof fn !== "function") { throw new TypeError("MyPromise constructor argument is not a function"); } this.state = "pending"; // 出初始化狀態(tài) this.value = undefined; // 初始化一個值, 用來存儲resolve或者reject的值 // 執(zhí)行 fn 方法 executeFn(fn, this); } MyPromise.prototype.then = function(onFullfilled, onRejected) { var res = undefined; var cb = this.state === "resolved" ? onFullfilled : onRejected; res = cb(this.value); } // 執(zhí)行 fn 方法 function executeFn(fn, promise) { var done = false; // 聲明一個變量, 防止resolve, reject連續(xù)調(diào)用 try { fn(function _resolve(value) { if(done) return; done = true; resolve(promise, value); }, function _reject(reason) { if(done) return; done = true; reject(promise, reason); }); } catch(err) { if(!done) { done = true; reject(promise, err); } } } function resolve(promise, value) { promise.state = "resolved"; promise.value = value; } function reject(promise, error) { promise.state = "rejected"; promise.value = error; }
這樣就實現(xiàn)了一個基本版的MyPromise,調(diào)用方法同Promise,如下:
new MyPromise((resolve, reject) => { // resolve("resolved"); reject("rejected"); }).then(res => { console.log(">>> res", res); }, err => { console.log(">>> err", err); });2.實現(xiàn)then的鏈?zhǔn)秸{(diào)用
原生Promise支持鏈?zhǔn)秸{(diào)用,并且then方法會返回一個新的Promise實例,第一個回調(diào)函數(shù)完成以后,會將返回結(jié)果作為參數(shù),傳入第二個回調(diào)函數(shù),所以我們可以修改下then方法,其余不變
MyPromise.prototype.then = function(onFullfilled, onRejected) { var self = this; var res = undefined; var cb = this.state === "resolved" ? onFullfilled : onRejected; var newPromise = new MyPromise(function(_resolve, _reject) { try { res = cb(self.value); _resolve(res); } catch(err) { _reject(err); } }); return newPromise; }
這樣的話,鏈?zhǔn)秸{(diào)用也就實現(xiàn)了,測試了下,沒啥問題
new MyPromise((resolve, reject) => { resolve("resolved"); // reject("rejected"); }).then(res => { console.log(">>> res", res); return "res1"; }, err => { console.log(">>> err", err); return "err1"; }).then(res2 => { console.log(">>> res2", res2); }, err2 => { console.log(">>> err2", err2); });3. 處理reolve函數(shù)的參數(shù)是MyPromise實例的情況以及處理then方法中前一個回調(diào)函數(shù)返回的也是一個MyPromise實例的情況
resolve函數(shù)的參數(shù)除了正常的值以外,還可能是另一個MyPromise實例,如下,這個時候p1的狀態(tài)決定了p2的狀態(tài)
const p1 = new MyPromise(function(resolve, reject) { // ... }); const p2 = new MyPromise(function(resolve, reject) { // ... resolve(p1); });
同樣,在then方法中,前一個回調(diào)函數(shù)有可能返回的也是一個MyPromise對象,這時后一個回調(diào)函數(shù)就會等待該MyPromise對象的狀態(tài)發(fā)生變化,才會被調(diào)用,例如:
const p1 = new MyPromise(function(resolve, reject) { // ... }); const p2 = new MyPromise(function(resolve, reject) { // ... resolve("p2 resolved"); }); p2.then(res1 => { console.log(">>> res1", res1); return p1; }, err1 => { console.log(">>> err1", err1); }).then(res2 => { console.log(">>> res2", res2); }, err2 => { console.log(">>> err2", err2); })
了解以上后,我們來實現(xiàn)它
function resolve(promise, value) { if(!handlePromise(promise, value)) return; // 增加這行 promise.state = "resolved"; promise.value = value; } /* * 增加一個函數(shù)用來處理返回值或者resolve的參數(shù)是promise的情況, 最后的返回值起個標(biāo)識作用 */ function handlePromise(promise, value) { if(value === promise) { reject(promise, "A promise cannot be resolved with itself"); return; } if(value && (typeof value === "object" || typeof value === "function")) { var then = value.then; if(typeof then === "function") { executeFn(then.bind(value), promise); return; } } return true; } // 同時then中增加一行代碼 MyPromise.prototype.then = function(onFullfilled, onRejected) { var self = this; var res = undefined; var cb = this.state === "resolved" ? onFullfilled : onRejected; var newPromise = new MyPromise(function(_resolve, _reject) { if(self.state === "pending") return; // 增加這行 try { res = cb(self.value); _resolve(res); } catch(err) { _reject(err); } }); return newPromise; }
到這里,就解決了以上2個問題,測試一下
const p = new MyPromise((resolve, reject) => { // resolve("resolve a promise"); reject("reject a promise"); }); // resolve參數(shù)是MyPromise實例 new MyPromise((resolve, reject) => { resolve(p); // reject("rejected"); }).then(res => { console.log(">>> res", res); return "res1"; }, err => { console.log(">>> err", err); return "err1"; }).then(res2 => { console.log(">>> res2", res2); }, err2 => { console.log(">>> err2", err2); }); // then第一個方法返回的是MyPromise實例 new MyPromise((resolve, reject) => { resolve("resolved"); // reject("rejected"); }).then(res => { console.log(">>> res", res); return p; }, err => { console.log(">>> err", err); return "err1"; }).then(res2 => { console.log(">>> res2", res2); }, err2 => { console.log(">>> err2", err2); });4. 實現(xiàn)異步的MyPromise
經(jīng)過前面3步,MyPromise的已經(jīng)實現(xiàn)了大半,接著我們來實現(xiàn)異步的MyPromise. 既然是異步,我們并不知道它什么時候結(jié)束,但是我們可以將它的異步回調(diào)存入一個數(shù)組,待它結(jié)束后執(zhí)行它,好吧,其實就是觀察者模式了
首先在構(gòu)造函數(shù)中加上一句
function MyPromise(fn) { // ...這里省略,加上下面這行 this.callbacks = []; // 存儲異步的回調(diào)方法 // 執(zhí)行 fn 方法 executeFn(fn, this); }
然后在resolve和reject方法中分別加上
function resolve(promise, value) { // ...這里省略,加上下面幾句 promise.callbacks.forEach(function(cb) { cb(); }); } function reject(promise, error) { // ...這里省略,加上下面幾句 promise.callbacks.forEach(function(cb) { cb(); }); }
最后在then方法中,將異步的回調(diào)發(fā)存入數(shù)組中
MyPromise.prototype.then = function(onFullfilled, onRejected) { // ...這里省略,不變 var newPromise = new MyPromise(function(_resolve, _reject) { // 狀態(tài)為pending時,將回調(diào)存入數(shù)組,因為then中方法也是異步執(zhí)行 // 所以用setTimeout,同時直接return if(self.state === "pending") { self.callbacks.push(function() { setTimeout(function() { // 這里需要再次判斷 cb = self.state === "resolved" ? onFullfilled : onRejected; try { res = cb(self.value); _resolve(res); } catch(err) { _reject(err); } }); }); return; } // then中是異步執(zhí)行 setTimeout(function() { try { res = cb(self.value); _resolve(res); } catch(err) { _reject(err); } }); }); return newPromise; }
到這里,異步的MyPromise也就實現(xiàn)了,then方法代碼有點亂,我們整理下
MyPromise.prototype.then = function(onFullfilled, onRejected) { // 這里刪除了一部分代碼 var self = this; var newPromise = new MyPromise(function(_resolve, _reject) { if(self.state === "pending") { self.callbacks.push(function() { // 同時將這部分的代碼抽成了以下方法 handleResolved(self, onFullfilled, onRejected, _resolve, _reject); }); return; } handleResolved(self, onFullfilled, onRejected, _resolve, _reject); }); return newPromise; } function handleResolved(promise, onFullfilled, onRejected, _resolve, _reject) { setTimeout(function() { var res = undefined; var cb = promise.state === "resolved" ? onFullfilled : onRejected; // 需要對cb進行判斷 if(typeof cb !== "function") { if(promise.state === "resolved") { _resolve(promise.value); } else { _reject(promise.value); } return; } try { res = cb(promise.value); _resolve(res); } catch(err) { _reject(err); } }); }
測試如下, 當(dāng)然沒啥問題了
const p = new MyPromise((resolve, reject) => { setTimeout(() => { resolve("resolve a promise"); // reject("reject a promise"); }, 2 * 1000); }); new MyPromise((resolve, reject) => { resolve(p); // reject("rejected"); }).then(res => { console.log(">>> res", res); return "res1"; }, err => { console.log(">>> err", err); return "err1"; }).then(res2 => { console.log(">>> res2", res2); }, err2 => { console.log(">>> err2", err2); });
到這里,一個大概的MyPromise也就基本實現(xiàn)完成了,整理后的完整代碼如下
function MyPromise(fn) { if (!(this instanceof MyPromise)) { throw new TypeError("MyPromise must be constructed via new"); } if (typeof fn !== "function") { throw new TypeError("MyPromise constructor argument is not a function"); } this.state = "pending"; // 初始化狀態(tài) this.value = undefined; // 初始化一個值, 用來存儲resolve或者reject的值 this.callbacks = []; // 存儲異步的回調(diào)方法 // 執(zhí)行 fn 方法 executeFn(fn, this); } MyPromise.prototype.then = function(onFullfilled, onRejected) { var self = this; var newPromise = new MyPromise(function(_resolve, _reject) { if(self.state === "pending") { self.callbacks.push(function() { handleResolved(self, onFullfilled, onRejected, _resolve, _reject); }); return; } handleResolved(self, onFullfilled, onRejected, _resolve, _reject); }); return newPromise; } function handleResolved(promise, onFullfilled, onRejected, _resolve, _reject) { setTimeout(function() { var res = undefined; var cb = promise.state === "resolved" ? onFullfilled : onRejected; if(typeof cb !== "function") { if(promise.state === "resolved") { _resolve(promise.value); } else { _reject(promise.value); } return; } try { res = cb(promise.value); _resolve(res); } catch(err) { _reject(err); } }); } // 執(zhí)行 fn 方法 function executeFn(fn, promise) { var done = false; // 聲明一個變量, 防止resolve, reject連續(xù)調(diào)用 try { fn(function _resolve(value) { if(done) return; done = true; resolve(promise, value); }, function _reject(reason) { if(done) return; done = true; reject(promise, reason); }); } catch(err) { if(!done) { done = true; reject(promise, err); } } } function resolve(promise, value) { if(!handlePromise(promise, value)) return; promise.state = "resolved"; promise.value = value; promise.callbacks.forEach(function(cb) { cb(); }); } function reject(promise, error) { promise.state = "rejected"; promise.value = error; promise.callbacks.forEach(function(cb) { cb(); }); } // 用來處理返回值或者resolve的參數(shù)是promise的情況, 最后的返回值起個標(biāo)識作用 function handlePromise(promise, value) { if(value === promise) { reject(promise, "A promise cannot be resolved with itself"); return; } if(value && (typeof value === "object" || typeof value === "function")) { var then = value.then; if(typeof then === "function") { executeFn(then.bind(value), promise); return; } } return true; }5. 最后來實現(xiàn)下MyPromise的其它方法
MyPromise.prototype.catch = function(onRejected) { return this.then(null, onRejected); } // 只要不是pending狀態(tài)都會執(zhí)行 MyPromise.prototype.finally = function(cb) { return this.then( function(value) { return MyPromise.resolve(cb()).then(function() { return value; }); }, function(err) { return MyPromise.resolve(cb()).then(function() { throw err; }); } ); } MyPromise.resolve = function(val) { return new MyPromise(function(resolve, reject) { resolve(val); }); } MyPromise.reject = function(err) { return new MyPromise(function(resolve, reject) { reject(err); }); } /* * all方法用于將多個 MyPromise 實例,包裝成一個新的 MyPromise 實例 * 只有全部實例都resolved,才會resolve; 只要其中一個rejected,就會reject * 參數(shù)可以不是數(shù)組,但必須具有 Iterator 接口, 同時里面的值可能也不是promise實例 */ MyPromise.all = function(promiseArr) { var args = [].slice.call(promiseArr); return new MyPromise(function(resolve, reject) { var arr = []; var resolveCount = 0; var argsLen = args.length; for(var i = 0; i < argsLen; i++) { handle(i, args[i]); } function handle(index, val) { MyPromise.resolve(val).then(function(value) { arr[index] = value; if(++resolveCount === argsLen) { resolve(arr); } }, reject); } }); } /* * race方法與all方法類似,只要其中一個實例狀態(tài)發(fā)生改變resolved / rejected即可 * 參數(shù)可以不是數(shù)組,但必須具有 Iterator 接口, 同時里面的值可能也不是promise實例 */ MyPromise.race = function(promiseArr) { var args = [].slice.call(promiseArr); return new MyPromise(function(resolve, reject) { for(var i = 0; i < args.length; i++) { MyPromise.resolve(args[i]).then(resolve, reject); } }); }
至此Promise的實現(xiàn)就算完成了,完整代碼的地址點這里
最后說點題外話,在面試的過程中,經(jīng)常會遇見面試官要求現(xiàn)場實現(xiàn)一個Promise,看了這篇文章后希望對你有所幫助,已經(jīng)能夠?qū)崿F(xiàn)一個Promise了,而對于面試中其他的promise的相關(guān)問題能夠輕松應(yīng)對
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/99981.html
摘要:上網(wǎng)查了一下好像是標(biāo)準(zhǔn)第二步開寫構(gòu)造函數(shù)什么都不想,先寫一個構(gòu)造函數(shù),就叫把。對構(gòu)造函數(shù)再次做修改。并且可以一個值。給下一個繼續(xù)調(diào)用。應(yīng)該是一個新的。最后的版本總結(jié)后來去看了看別人實現(xiàn)的方法。 我能不能在不看別人怎么實現(xiàn)promise的情況下,自己實現(xiàn)一個promise? 都8102年為什么還要寫promise實現(xiàn)? ? 年前和年后面試了幾家公司, 雖然都掛了… 但是都談到了一個...
摘要:本文僅限瀏覽器環(huán)境測試,環(huán)境可能會不一致狀態(tài)一個實例只能處于三種狀態(tài)中的一種。每次創(chuàng)建的實例都會處于狀態(tài),并且只能由變?yōu)榛驙顟B(tài)。可以認為在實現(xiàn)里與中的都為解決程序。 前言 Promise作為ES6極為重要的一個特性,將我們從無限的回調(diào)地獄中解脫出來,變?yōu)殒準(zhǔn)降木帉懟卣{(diào),大大提高的代碼的可讀性。 使用Promise是極為簡單的,但只停留在會使用階段還是會讓我們不知不覺踩到一些坑的。本文會...
摘要:從最開始的到封裝后的都在試圖解決異步編程過程中的問題。為了讓編程更美好,我們就需要引入來降低異步編程的復(fù)雜性。寫一個符合規(guī)范并可配合使用的寫一個符合規(guī)范并可配合使用的理解的工作原理采用回調(diào)函數(shù)來處理異步編程。 JavaScript怎么使用循環(huán)代替(異步)遞歸 問題描述 在開發(fā)過程中,遇到一個需求:在系統(tǒng)初始化時通過http獲取一個第三方服務(wù)器端的列表,第三方服務(wù)器提供了一個接口,可通過...
摘要:意味著操作成功完成。狀態(tài)的對象可能觸發(fā)狀態(tài)并傳遞一個值給相應(yīng)的狀態(tài)處理方法,也可能觸發(fā)失敗狀態(tài)并傳遞失敗信息。測試用例測試用例方法返回一個帶有拒絕原因參數(shù)的對象。 Promise基本用法 Promise 對象是一個代理對象,被代理的值在Promise對象創(chuàng)建時可能是未知的。 它允許你為異步操作的成功和失敗分別綁定相應(yīng)的處理方法(handlers)。 這讓異步方法可以像同步方法那樣返回值...
摘要:鏈?zhǔn)秸{(diào)用在的使用中,我們一定注意到,是可以鏈?zhǔn)秸{(diào)用的很顯然,要實現(xiàn)鏈?zhǔn)秸{(diào)用,方法的返回值也必須是一個對象,這樣才能再次在后面調(diào)用。一種情況下,前一個的或者的返回值是普通的對象,這種情況下我們目前的可以正確處理。 本文同步自我的個人博客: http://mly-zju.github.io/ 眾所周知javascript語言的一大特色就是異步,這既是它的優(yōu)點,同時在某些情況下也帶來了一些的...
摘要:所以,這篇文章我會帶大家從零開始,手寫一個基本能用的。首先,規(guī)定對象是一個構(gòu)造函數(shù),用來生成實例。然后,這個構(gòu)造函數(shù)接受一個函數(shù)作為參數(shù),該函數(shù)的兩個參數(shù)分別是和。對象通過自身的狀態(tài),來控制異步操作。 剛開始寫前端的時候,處理異步請求經(jīng)常用callback,簡單又順手。后來寫著寫著就拋棄了callback,開始用promise來處理異步問題。promise寫起來確實更加優(yōu)美,但由于缺乏...
閱讀 3093·2021-09-22 15:20
閱讀 2611·2019-08-30 15:54
閱讀 1975·2019-08-30 14:06
閱讀 3123·2019-08-30 13:05
閱讀 2467·2019-08-29 18:36
閱讀 581·2019-08-29 15:10
閱讀 533·2019-08-29 11:17
閱讀 833·2019-08-28 18:11