摘要:如何寫一個符合規范的實現前言是異步編程的一種解決方案從語法上講,是一個對象,從它可以獲取異步操作的消息從本意上講,它是承諾,承諾它過一段時間會給你一個結果。
如何寫一個符合promiseA+規范的promise實現 前言
Promise 是異步編程的一種解決方案:編寫符合promiseA+規范的promise實現
從語法上講,promise是一個對象,從它可以獲取異步操作的消息;從本意上講,它是承諾,承諾它過一段時間會給你一個結果。
promise有三種狀態:pending(等待態),fulfiled(成功態),rejected(失敗態);狀態一旦改變,就不會再變。創造promise實例后,它會立即執行。
在實現之前,可以先看一下Promise A plus規范
1. 創建promise構造函數這里先實現promise最基本的功能:promise創建后立即執行;在then時執行相應的函數;捕獲錯誤立即變成reject態。
// promise里只有一個參數,叫executor(執行器) function Promise(executor) { let self = this; self.status = "pending";//等待態 self.value = undefined;//默認成功的值 self.err = undefined;//默認失敗的值 function resolve(value) { if (self.status === "pending") { self.status = "resolved"; self.value = value; } } function reject(err) { if (self.status === "pending") { self.status = "rejected"; self.err = err; } } try {//捕獲時發生異常,直接變成reject態,拋出錯誤 executor(resolve, reject);//promise實例創建后,立即執行 } catch (error) { reject(error); } } //在prototype上定義then實例方法 Promise.prototype.then = function (onFulfilled, onRejected) { let self = this; if (self.status === "resolved") { onFulfilled(self.value); } if (self.status === "rejected") { onRejected(self.err); } }
這里我們先測試一下我們的Promise
這里便實現了基本功能,前面說過Promise 是異步編程的一種解決方案;
我們加個異步邏輯運行一下:
我們都知道異步代碼并不會立即執行,這時既不是resolved也不是rejected,而是pending。
在之前的狀態判斷里面,正好丟了一個pending狀態。OK,這時需要在then里判斷當status為pending時,先將onFulfilled, onRejected存入數組里,當status改變時,再遍歷數組讓里面的函數依次執行,看代碼。
(1)申明兩個存放onFulfiled,onRejected的數組
function Promise(resolver) { let self = this; self.status = "pending";//等待態 self.value = undefined;//默認成功的值 self.err = undefined;//默認失敗的值 self.onResolvedCallbacks = []; // 存放then成功的回調 self.onRejectedCallbacks = []; // 存放then失敗的回調 function resolve(value) { if (self.status === "pending") { self.status = "resolved"; self.value = value; self.onResolvedCallbacks.forEach(fn=>{//調用resolve時,依次執行數組里的函數 fn(); }) } } function reject(err) { if (self.status === "pending") { self.status = "rejected"; self.err = err; self.onRejectedCallbacks.forEach(fn=>{ fn(); }) } } try {//捕獲時發生異常,直接拋出錯誤 resolver(resolve, reject);//promise實例創建后,立即執行它的方法 } catch (error) { reject(error) } }
(2)接著在then方法里添加pending的判斷
Promise.prototype.then = function (onFulfilled, onRejected) { let self = this; if (self.status === "resolved") { onFulfilled(self.value); } if (self.status === "rejected") { onRejected(self.err); } if(self.status==="pending"){// 此時沒resolved,也沒rejectd self.onResolvedCallbacks.push(()=>{ onFulfilled(self.value); }); self.onRejectedCallbacks.push(()=>{ onRejected(self.err); }) } }
再看剛剛的異步邏輯
1s后就執行成功了,是不是很神奇,再看下面:
3. Promise鏈式調用(1)規范里說在同一個promise里then可以被多次調用。
(2)jquery能實現鏈式調用靠的是返回this,而promise不能返回this,規范里又說它返回的是一個新的Promise實例(注意,不是原來那個Promise實例);
在then里新建一個promise2并為每一個狀態包一個Promise
寫到這里,再來看看規范,規范里說道
(1)x可能是一個promise;
(2)可能是一個對象或者方法;
(3)也有可能是一個普通的值。
這時需要一個方法來處理x
3.1 對onFulfilled和onRejected的返回值進行處理于是引入一個處理方法resolvePromise(promise2, x, resolve, reject);
這里需要注意一下,有些人寫的promise可能會既調用成功,又調用失敗,如果兩個都調用先調用誰另一個就忽略掉。
于是增加一個判斷called表示是否調用過成功或者失敗,看代碼:
function resolvePromise(promise2, x, resolve, reject) { if (promise2 === x) {//promise2和x不能相同 return reject(new TypeError("循環引用了")) } let called;// 表示是否調用過成功或者失敗 //這里對x的類型進行判斷 if (x !== null && (typeof x === "object" || typeof x === "function")) { try { // 判斷x是不是promise,如果x是對象并且x的then方法是函數我們就認為他是一個promise let then = x.then; if (typeof then === "function") { then.call(x, function (y) { if (called) return called = true // y可能還是一個promise,在去解析直到返回的是一個普通值 resolvePromise(promise2, y, resolve, reject) }, function (err) { //失敗 if (called) return called = true reject(err); }) } else { resolve(x) } } catch (e) { if (called) return called = true; reject(e); } } else { // 說明是一個普通值1 resolve(x); // 表示成功了 } }
相應的將前面的代碼進行一些更改
如果在then中什么都不傳,值會穿透到最后調用的時候;
這時需要在then里給onFulfilled和onRejected寫一個默認的函數
onFulfilled = typeof onFulfilled === "function" ? onFulfilled : function (value) { return value; } onRejected = typeof onRejected === "function" ? onRejected : function (err) { throw err;//這里需要拋出錯誤,不能return err,否則會在下一次調用成功態 }5. then的異步實現
規范里要求,所有的onFulfilled和onRejected都要確保異步執行
這里以resolve為例,寫一個setTimeout():
在使用promise的過程中,我們都需要先new Promise(),比如說:
function read() { let fs = require("fs"); let promise = new Promise(function(resolve,reject){ fs.readFile("./1.txt","utf8",function(err,data){ if(err) reject(err); resolve(data); }) }); return promise }
在Promise中,它為我們提供了一個語法糖Promise.defer,用Promise.defer只需這樣寫:
function read() { let defer = Promise.defer() require("fs").readFile(".//1.txt", "utf8", function (err, data) { if(err) defer.reject(err); defer.resolve(data); }) return defer.promise; }
為此,再為我們的Promise加一個defer方法:
Promise.defer = Promise.deferred = function () { let dfd = {}; dfd.promise = new Promise(function (resolve, reject) { dfd.resolve = resolve; dfd.reject = reject; }); return dfd }
在這里,我們基本實現了一個比較完整的promise;當然Promise還有許多靜態方法,還有js的異步發展史,這些可以在下一次進行討論。
完整代碼:
// promise里只有一個參數,叫executor(執行器) function Promise(executor) { let self = this; self.status = "pending";//等待態 self.value = undefined;//默認成功的值 self.err = undefined;//默認失敗的值 self.onResolvedCallbacks = []; // 存放then成功的回調 self.onRejectedCallbacks = []; // 存放then失敗的回調 function resolve(value) { if (self.status === "pending") { self.status = "resolved"; self.value = value; self.onResolvedCallbacks.forEach(function (fn) { fn(); }); } } function reject(err) { if (self.status === "pending") { self.status = "rejected"; self.err = err; self.onRejectedCallbacks.forEach(function (fn) { fn(); }); } } try {//捕獲時發生異常,直接變成reject態,拋出錯誤 executor(resolve, reject);//promise實例創建后,立即執行 } catch (error) { reject(error); } } function resolvePromise(promise2, x, resolve, reject) { if (promise2 === x) {//promise2和x不能相同 return reject(new TypeError("循環引用了")) } let called;// 表示是否調用過成功或者失敗 //這里對x的類型進行判斷 if (x !== null && (typeof x === "object" || typeof x === "function")) { try { // 判斷x是不是promise,如果x是對象并且x的then方法是函數我們就認為他是一個promise let then = x.then; if (typeof then === "function") { then.call(x, function (y) { if (called) return called = true // y可能還是一個promise,在去解析直到返回的是一個普通值 resolvePromise(promise2, y, resolve, reject) }, function (err) { //失敗 if (called) return called = true reject(err); }) } else { resolve(x) } } catch (e) { if (called) return called = true; reject(e); } } else { // 說明是一個普通值1 resolve(x); // 表示成功了 } } //在prototype上定義then實例方法 Promise.prototype.then = function (onFulfilled, onRejected) { onFulfilled = typeof onFulfilled === "function" ? onFulfilled : function (value) { return value; } onRejected = typeof onRejected === "function" ? onRejected : function (err) { throw err;//這里需要拋出錯誤,不能return err,否則會在下一次調用成功態 } let self = this; let promise2; //返回的promise if (self.status === "resolved") { promise2 = new Promise(function (resolve, reject) { setTimeout(function () { try { let x = onFulfilled(self.value); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }) }) } if (self.status === "rejected") { promise2 = new Promise(function (resolve, reject) { setTimeout(function () { try { let x = onRejected(self.err); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }) }) } // 當調用then時可能沒成功 也沒失敗 if (self.status === "pending") { promise2 = new Promise(function (resolve, reject) { // 此時沒有resolve 也沒有reject self.onResolvedCallbacks.push(function () { setTimeout(function () { try { let x = onFulfilled(self.value); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e) } }) }); self.onRejectedCallbacks.push(function () { setTimeout(function () { try { let x = onRejected(self.err); resolvePromise(promise2, x, resolve, reject); } catch (e) { reject(e); } }) }); }) } return promise2; } Promise.defer = Promise.deferred = function () { let dfd = {}; dfd.promise = new Promise(function (resolve, reject) { dfd.resolve = resolve; dfd.reject = reject; }); return dfd } module.exports = Promise;7.Promise測試
npm i -g promises-aplus-tests promises-aplus-tests Promise.js
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/107622.html
摘要:以上代碼,可以完美通過所有用例。在的函數中,為何需要這個同樣是因為規范中明確表示因此我們需要這樣的來確保只會執行一次。其他情況,直接返回以該值為成功狀態的對象。 Promise是前端面試中的高頻問題,我作為面試官的時候,問Promise的概率超過90%,據我所知,大多數公司,都會問一些關于Promise的問題。如果你能根據PromiseA+的規范,寫出符合規范的源碼,那么我想,對于面試...
摘要:今天我們來自己手寫一個符合規范的庫。是異步編程的一種解決方案,比傳統的解決方案回調函數和事件更合理和更強大。我們可以看到,其實就是一個構造函數。所以說我們的數組里存的是一個一個的的回調函數,也就是一個一個。 今天我們來自己手寫一個符合PromiseA+規范的Promise庫。大家是不是很激動呢?? showImg(https://segmentfault.com/img/bV6t4Z?...
摘要:我們都知道,方法中有和兩個回調函數,所以我們要處理一下這兩個回調函數。我們實現了異步調用,在方法中返回或者值,實現了方法中可以沒有回調函數也能把執行結果傳入下一次的方法中。 Hello everybody。我又來啦,還記得我們上一張實現的內容嗎? showImg(https://segmentfault.com/img/bV6UaU?w=102&h=95); 上一張我們實現了一個簡單的...
摘要:不同的的實現需要可以相互調用,搞清楚了標準之后,開始動手吧構造函數產生一個對象有很多種方法,構造函數是看起來最面向對象的一種,而且原生實現也是使用的構造函數,因此我也決定使用構造函數的方法。 -- What i cant create, i dont understant 前言 實現Promise的目的是為了深入的理解Promies,以在項目中游刃有余的使用它。完整的代碼見gitHub...
摘要:嗝首先,我們通過字面可以看出來是一種解決方案,而且還有兩種傳統的解決方案回調函數和事件,,那么我們就來先聊聊這兩種方案。 前言 雖然今年已經18年,但是今天還是要繼續聊聊ES6的東西,ES6已經過去幾年,可是我們對于ES6的語法究竟是掌握了什么程度,是了解?會用?還是精通?相信大家和我一樣都對自己有著一個提升的心,對于新玩具可不能僅僅了解,對于其中的思想才是最吸引人的,所以接下來會通過...
閱讀 3638·2021-11-19 09:40
閱讀 3096·2019-08-30 15:54
閱讀 2314·2019-08-30 15:44
閱讀 3196·2019-08-29 15:35
閱讀 3331·2019-08-29 12:22
閱讀 2861·2019-08-28 18:01
閱讀 3141·2019-08-26 13:54
閱讀 902·2019-08-26 12:24