這篇文章主要介紹“如何理解Promise”,在日常操作中,相信很多人在如何理解Promise問(wèn)題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”如何理解Promise”的疑惑有所幫助!接下來(lái),請(qǐng)跟著小編一起來(lái)學(xué)習(xí)吧!
目前創(chuàng)新互聯(lián)已為上1000家的企業(yè)提供了網(wǎng)站建設(shè)、域名、虛擬空間、網(wǎng)站托管運(yùn)營(yíng)、企業(yè)網(wǎng)站設(shè)計(jì)、仙游網(wǎng)站維護(hù)等服務(wù),公司將堅(jiān)持客戶導(dǎo)向、應(yīng)用為本的策略,正道將秉承"和諧、參與、激情"的文化,與客戶和合作伙伴齊心協(xié)力一起成長(zhǎng),共同發(fā)展。
大概的架子
通過(guò)我們經(jīng)常寫(xiě)的 promise 語(yǔ)法,我們可以先寫(xiě)一個(gè)大概的架子出來(lái),promise 接受回調(diào),并且調(diào)用,自身帶有三種狀態(tài),pendding, onFulfilled, onRejected,并且 resolve 這個(gè)函數(shù)可以讓 pendding 狀態(tài)變成 onFulfilled 狀態(tài),同理 reject 函數(shù)可以讓 pendding 狀態(tài)變成 onRejected 狀態(tài)。我們先把上面描述部分實(shí)現(xiàn)了。
const PromiseCopy = function (fn) { this.info = { status: "pending", value: "", }; const self = this; self.onFulfilledArr = []; // then函數(shù)里面的第一個(gè)回調(diào)函數(shù)的集合 self.onRejectedArr = []; // then函數(shù)里面的第二個(gè)回調(diào)函數(shù)的集合 const resolve = function (value) { // 加這個(gè)判斷是為了表示,只有在pendding狀態(tài)下才會(huì)去執(zhí)行 // 狀態(tài)已經(jīng)變成onFulfilled之后就不能再去改變了 // 符合PromiseA+中的2.1.2.1 if (self.info.status === "pending") { self.info.status = "onFulfilled"; self.info.value = value; self.onFulfilledArr.forEach((fn) => fn(value)); } }; // 和上面同理符合PromiseA+,2.1.3.1 const reject = function (value) { if (self.info.status === "pending") { self.info.status = "onRejected"; self.info.value = value; self.onRejectedArr.forEach((fn) => fn(value)); } }; fn(resolve, reject); };resolve 的附加實(shí)現(xiàn)
其實(shí)寫(xiě)到這里我們的 resolve 函數(shù)還是有一些功能沒(méi)有實(shí)現(xiàn)的, 我們知道 調(diào)用 resolve(x), x 的值有好幾種情況,如下
如果 x 是 Promise 實(shí)例本身,則拋出錯(cuò)誤
如果 x 是一個(gè) Promise 對(duì)象,那么 then 函數(shù)的執(zhí)行取決這個(gè) x 的狀態(tài),如果 x 也調(diào)用 resolve(y),其中 y 也是一個(gè) promise 對(duì)象.那么 then 函數(shù)的執(zhí)行取決于這個(gè) promise 對(duì)象,依次類(lèi)推,直到最后一個(gè) promise 狀態(tài)更改
如果 x 是一個(gè) thenable 對(duì)象,就是一個(gè)對(duì)象包含 then 這個(gè)屬性,或者是一個(gè)函數(shù)包含一個(gè) then 的靜態(tài)方法,那么直接執(zhí)行 then 函數(shù)
如果 x 是一個(gè)普通值,直接變成 onFulfilled 狀態(tài),執(zhí)行后面的 then 函數(shù)
思考
網(wǎng)上實(shí)現(xiàn)的大部分 resolve 都是我上面的代碼,但是根據(jù)規(guī)范,resolve 函數(shù)里面應(yīng)該是要判斷上面幾點(diǎn)的,所以我們上面寫(xiě)的代碼是有誤的
還有一個(gè)問(wèn)題是,我們需要在 resolve 函數(shù)里面判斷 x 是不是實(shí)例的本身,但是正常的 resolve 函數(shù)我們經(jīng)常是傳入一個(gè)參數(shù),所以中間肯定是有一個(gè)中間函數(shù)的,看下面的代碼
const PromiseCopy = function (fn) { this.info = { status: "pending", value: "", }; const self = this; self.onFulfilledArr = []; // then函數(shù)里面的第一個(gè)回調(diào)函數(shù)的集合 self.onRejectedArr = []; // then函數(shù)里面的第二個(gè)回調(diào)函數(shù)的集合 // _resolve 是我們經(jīng)常調(diào)用的resolve // 但是真正實(shí)現(xiàn)的應(yīng)該是里面的resolve const _resolve = function (value) { // 這個(gè)函數(shù)得改變一下 // PromiseCopy一旦被實(shí)例化,那么self就是實(shí)例本身了 resolve(self, value); }; // 此時(shí)我們就可以在resolve進(jìn)行判斷了 const resolve = function (promise, value) { let ifexec = false; // 首先判斷value是不是promise本身 if (value === promise) { // 一定要用TypeError寫(xiě) 不然promises-aplus-tests跑不通 // 切記這是第一個(gè)坑,promises-aplus-tests只認(rèn)TypeError這種錯(cuò)誤形式 reject(new TypeError("A promise cannot be onFulfilled with itself.")); } // value是一個(gè)thenable對(duì)象 // 這個(gè)要Object.prototype.toString.call(value) === "[object Object]"判斷 // 不然resolve([])有問(wèn)題,不知道是不是我實(shí)現(xiàn)問(wèn)題 if ( value && (Object.prototype.toString.call(value) === "[object Object]" || typeof value === "function") ) { // var promise1 = Promise.resolve(dump).then(function () { // return { // then: (resolve, reject) => { // setTimeout(() => { // resolve({ // then: (resolve, reject) => { // resolve("aa111a"); // throw "other"; // }, // }); // }); // }, // }; // }); // promise1.then( // (res) => { // console.log(res === "aa111a"); // console.log("aaa"); // }, // (res) => { // console.log(res); // console.log("error"); // } // ); // 這里的try--catch一定要加 ,不然會(huì)promises-aplus-tests會(huì)一直報(bào)錯(cuò),這是第三個(gè)大坑 // 因?yàn)閜romises-aplus-test測(cè)試?yán)锩嬗羞@一條的 // 看上面注釋例子 try { // 拿到then函數(shù) const then = value.then; // 如果then是一個(gè)函數(shù)則執(zhí)行這個(gè)函數(shù) if (typeof then === "function") { // 為什么要.call(value, x, y) 你們可以自己試一下原生的Promise在這種情況下this指向的就是value,所以要綁定 // 因?yàn)閠hen我們已經(jīng)拿出來(lái)了then = value.then,直接調(diào)用then(),this就指向的window // 為什么后面還需要綁定兩個(gè)函數(shù)了 // 根據(jù)原生的Promise可知,thenable中的then函數(shù)可以接受兩個(gè)函數(shù)resolve,reject // 只有手動(dòng)調(diào)用了resolve和reject才會(huì)執(zhí)行后面的.then操作,具體大家自己操作下 then.call( value, function (value) { if (ifexec) { return; } // ifexec這個(gè)一定要加,不然也會(huì)報(bào)200ms錯(cuò)誤,第四個(gè)大坑 // 目的是為了不讓多次執(zhí)行,語(yǔ)言無(wú)法表達(dá)看下面的例子 // var promise1 = Promise.resolve(dump).then(function () { // return { // then: (resolve, reject) => { // resolve("aa111a"); // resolve("aa111a"); // }, // }; // }); ifexec = true; resolve(promise, value); }, function (value) { if (ifexec) { return; } ifexec = true; reject(value); } ); return; } } catch (e) { if (ifexec) { return; } ifexec = true; reject(e); } } // 下面這一點(diǎn)非常的重要,是async,await 和一些插件比如saga的核心 // 就是如果x是一個(gè)promise對(duì)象,那么then的執(zhí)行取決于x的狀態(tài) // 還有這一個(gè)判斷一定要放在這里,不要和上面的換 不然promises-aplus-tests會(huì)報(bào)一個(gè)超過(guò)200ms的錯(cuò)誤,切記這是第二個(gè)坑 if (value && value instanceof PromiseCopy && value.then === promise.then) { // 將promise的onFulfilledArr給到value // 但是還沒(méi)有那么簡(jiǎn)單我們要明白兩點(diǎn) // 如果value這個(gè)promise已經(jīng)不是pendding,我們給了他也沒(méi)有用,所以需要直接調(diào)用 if (value.info.status === "pending") { value.onFulfilledArr = self.onFulfilledArr; value.onRejectedArr = self.onRejectedArr; } // 如果value狀態(tài)是onFulfilled if (value.info.status === "onRejected") { self.info.value = value.info.value; self.onRejectedArr.forEach((fn) => fn(value.info.value)); } // 如果value狀態(tài)是reject if (value.info.status === "onFulfilled") { self.info.value = value.info.value; self.onFulfilledArr.forEach((fn) => fn(value.info.value)); } return; } // 如果是一個(gè)普通的值 // 加這個(gè)判斷是為了表示,只有在pendding狀態(tài)下才會(huì)去執(zhí)行 // 狀態(tài)已經(jīng)變成onFulfilled之后就不能再去改變了 // 符合PromiseA+中的2.1.2.1 if (self.info.status === "pending") { self.info.status = "onFulfilled"; self.info.value = value; self.onFulfilledArr.forEach((fn) => fn(value)); } }; // 和上面同理符合PromiseA+,2.1.3.1 // reject沒(méi)有resolve那么多規(guī)則,比較簡(jiǎn)單 const reject = function (value) { if (self.info.status === "pending") { self.info.status = "onRejected"; self.info.value = value; self.onRejectedArr.forEach((fn) => fn(value)); } }; // 此時(shí)fn調(diào)用的是_reoslve // 這個(gè)try catch主要是實(shí)現(xiàn)promiseCopy.prototype.catch try { fn(_resolve, reject); } catch (e) { setTimeout(() => { self.onRejectedArr.forEach((fn) => fn(e)); }); } };then 的實(shí)現(xiàn)
我們上面介紹的是 promise 的 resolve 用法,promise 還有一個(gè)基本用法就是后面接 then,因?yàn)槭?then 所以我們想到的是這個(gè) then 方法掛在到原型上的,那么 new PromiseCopy 的時(shí)候就可以得到這個(gè) then。then 里面是兩個(gè)函數(shù),一個(gè)是 onFulfilled 后執(zhí)行的回調(diào),一個(gè)是 onRejected 后執(zhí)行的回調(diào)。現(xiàn)在的問(wèn)題是他是怎么做到 then 里面的函數(shù)是在 resolve 和 reject 后執(zhí)行的?這種推遲執(zhí)行或者說(shuō)在某種情況下去執(zhí)行我們想到的就是觀察者模式了。下面用代碼把上面的話實(shí)現(xiàn)一遍,在代碼里面會(huì)寫(xiě)詳細(xì)一點(diǎn)的注釋。
PromiseCopy.prototype.then = function (onFulfilled, onRejected) { const self = this; // 這里要判斷下,如果PromiseCopy是resolve了那么就直接執(zhí)行onFulfilled if (self.info.status === "onFulfilled") { setTimeout(() => { onFulfilled(self.info.value); }); } if (self.info.status === "onRejected") { setTimeout(() => { onRejected(self.info.value); }); } // 根據(jù)PromiseA+中的2.2.1.1和2.2.1.2,onFulfilled和onRejected必須是函數(shù),不然就會(huì)被忽略 if (typeof onFulfilled === "function") { self.onFulfilledArr.push(() => { setTimeout(() => { onFulfilled(self.info.value); }); }); } if (typeof onRejected === "function") { self.onRejectedArr.push(() => { setTimeout(() => { onRejected(self.info.value); }); }); } // 根據(jù)PromiseA+ 2.2.7規(guī)范 then函數(shù)必須返回一個(gè)promise對(duì)象 return new PromiseCopy((resolve, reject) => {}); };then 的額外實(shí)現(xiàn)
上面實(shí)現(xiàn)的 then 也是一個(gè)簡(jiǎn)單的用法,不過(guò)根據(jù) PromiseA+的規(guī)范這個(gè) then 函數(shù)還有幾個(gè)點(diǎn)沒(méi)有實(shí)現(xiàn),看代碼解釋
promise2 = promise1.then(onFulfilled, onRejected); promise2.then(onFulfilled, onRejected);
promise1.then 中的 onFulfilled,onRejected 函數(shù)如果返回一個(gè) x,那么當(dāng)作[[Resolve]](promise2, x)來(lái)處理,就跟上面的 resolve 一樣處理,注意如果函數(shù)什么都沒(méi)有返回,就是返回的 undefined
promise1.then 函數(shù)中的兩個(gè)回調(diào)函數(shù)只要有一個(gè)報(bào)錯(cuò),那么直接調(diào)用 promise2.then 函數(shù)中的錯(cuò)誤回調(diào)
如果 promise1.then 的第一個(gè)回調(diào)不是函數(shù),并且 promise1 調(diào)用的是 resolve,那么 promise2.then 的第一個(gè)回調(diào)參數(shù)是 promise1 中 resolve 函數(shù)的拋出值
同理,如果 promise1.then 第二個(gè)回調(diào)不是函數(shù),并且 promise1 調(diào)用的是 reject,那么 promise2.then 中的錯(cuò)誤回調(diào)就會(huì)執(zhí)行
思考
如果像上面這么說(shuō)的話,這個(gè)新拋出來(lái)的 promise 何時(shí)調(diào)用這個(gè) resolve 或者 reject 是一個(gè)關(guān)鍵, 并且這個(gè)拋出的 promise 的執(zhí)行還得看 onFulfilled 和 onRejected 返回值,這一點(diǎn)當(dāng)時(shí)寫(xiě) promise 的時(shí)候想了很久,不知道如何組織,后來(lái)實(shí)在想不出來(lái),看了下網(wǎng)上很多文章,發(fā)現(xiàn)這些邏輯都是在 PromiseCopy 主體里面實(shí)現(xiàn)的。
return new PromiseCopy((resolve, reject) => {});
then 實(shí)現(xiàn)加強(qiáng)版
PromiseCopy.prototype.then = function (onFulfilled, onRejected) { const self = this; // 這個(gè)一定要這么寫(xiě)目的為了讓值傳遞 onFulfilled = typeof onFulfilled === "function" ? onFulfilled : (val) => val; // 這個(gè)一定要這么寫(xiě),一定要拋出一個(gè)錯(cuò)throw err onRejected = typeof onRejected === "function" ? onRejected : (err) => { throw err; }; const newnewPromise = new PromiseCopy((resolve, reject) => { if (self.info.status === "onFulfilled") { setTimeout(() => { try { // 如果onFulfilled不是一個(gè)函數(shù)resolve--self.info.value let value = self.info.value; // 這個(gè)注釋不要,留著只是為了記錄當(dāng)時(shí)的思路 // 這個(gè)加判斷是為了防止then函數(shù)逇回調(diào)不是一個(gè)函數(shù),,是一個(gè)字符串 // if (typeof onFulfilled === "function") { // value = onFulfilled(value); // } value = onFulfilled(value); // 這里要做一個(gè)[[Resolve]](promise2, x)處理了 // 因?yàn)閞esolve里面直接做了,所以直接調(diào)用,和網(wǎng)上的一些實(shí)現(xiàn)有點(diǎn)不一樣 // 他們是提取了一個(gè)resolvePromise函數(shù)調(diào)用,我是直接調(diào)用了resolve resolve(value); } catch (e) { reject(e); } }); } // 注意這里根據(jù)上面可知onFulfilled,onRejected拋出的值都要經(jīng)過(guò)[[Resolve]](promise2, x) // 這和resolve,reject不一樣,promise中resolve才走[[Resolve]](promise2, x),reject不走 if (self.info.status === "onRejected") { setTimeout(() => { try { let { value } = self.info; value = onRejected(self.info.value); resolve(value); } catch (e) { reject(e); } }); } // 如果是pending狀態(tài)也需要push if (self.info.status === "pending") { self.onFulfilledArr.push((data) => { setTimeout(() => { try { let value = data; value = onFulfilled(value); resolve(value); } catch (e) { reject(e); } }); }); self.onRejectedArr.push((data) => { setTimeout(() => { try { let value = data; value = onRejected(data); resolve(value); } catch (e) { reject(e); } }); }); } }); return newPromise; };小結(jié)
到這里 promise 的主體實(shí)現(xiàn)已經(jīng)完成了,下面是測(cè)試結(jié)果


Promise 其他靜態(tài)方法
Promise.resolve
PromiseCopy.resolve = function (data) { return new PromiseCopy((resolve, reject) => { resolve(data); }); };reject
Promise.reject = function (reason) { return new Promise((resolve, reject) => { reject(reason); }); };Promise.all
這個(gè)方法有幾個(gè)特點(diǎn)如下
該方法接受一個(gè)數(shù)組,數(shù)組每一個(gè)元素都是一個(gè) promise 對(duì)象
只有所有 promise 都是 onFulfilled 的時(shí)候才會(huì)執(zhí)行 then 回調(diào),并且結(jié)果順序和數(shù)組的一致
如果其中一個(gè) promise 發(fā)生了 reject 那么就會(huì)返回這個(gè)值
PromiseCopy.all = function (data) { let count = 0; // 記錄調(diào)用次數(shù) let total = data.length; let result = []; return new PromiseCopy((resolve, reject) => { for (let i = 0; i < total; i++) { data[i].then( (res) => { result.push(res); ++count; if (count === totla) { resolve(result); } }, (res) => { return reject(res); } ); } }); };Promise.race
這個(gè)方法也有以下幾個(gè)特點(diǎn)
這個(gè)方法也是接受數(shù)組,數(shù)組的元素是 promise
他只返回最快的那一個(gè) promise 的值
就算有錯(cuò)誤也會(huì)返回最快那一個(gè) promise 的值
PromiseCopy.race = function (data) { const total = data.length; return new PromiseCopy((resolve, reject) => { for (let i = 0; i < total; i++) { data[i].then( (res) => { resolve(res); }, (res) => { return reject(res); } ); } }); };catch 方法
PromiseCopy.prototype.catch = function (onRejected) { // 能到catch里面來(lái)的一定是走的reject的 // 而且狀態(tài)一定是pendding const self = this; const newnewPromise = new PromiseCopy((resolve, reject) => { if (self.info.status === "onRejected") { try { setTimeout(() => { let { value } = self.info; if (typeof onRejected === "function") { value = onRejected(self.info.value); } resolve(value); }); } catch (e) { rejetc(e); } } if (self.info.status === "pending") { self.onRejectedArr.push((data) => { setTimeout(() => { try { let value = data; if (typeof onRejected === "function") { value = onRejected(data); } resolve(value); } catch (e) { reject(e); } }); }); } }); return newPromise; }; // 后來(lái)發(fā)現(xiàn)catch有一個(gè)簡(jiǎn)單的實(shí)現(xiàn)方法 // 沒(méi)有刪除上面就是為了記錄思路過(guò)程 Promise.prototype.catch = function (onRejected) { return this.then(null, onRejected); };deferred
這個(gè)是 Promise 提供的一個(gè)快捷使用,自己實(shí)現(xiàn) promise 的時(shí)候一定要加,不然 promises-aplus-tests promise.js 跑不過(guò)
PromiseCopyPromiseCopy.defer = PromiseCopy.deferred = function () { let dfd = {}; dfd.promise = new PromiseCopy((resolve, reject) => { dfd.resolve = resolve; dfd.reject = reject; }); return dfd; };到此,關(guān)于“如何理解Promise”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注創(chuàng)新互聯(lián)網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)?lái)更多實(shí)用的文章!
分享標(biāo)題:如何理解Promise
標(biāo)題路徑:http://www.chinadenli.net/article4/gpddie.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供品牌網(wǎng)站設(shè)計(jì)、定制開(kāi)發(fā)、服務(wù)器托管、網(wǎng)站制作、網(wǎng)站設(shè)計(jì)公司、微信小程序
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)