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