Promise做爲面試中的經典考題,咱們必定要深入學習和理解它! Promise有什麼用呢?答:咱們拿它解決異步回調問題。Pomise是ES6裏面新增的一種異步編程的解決方案。如今這個promise在面試中感受就像「css清除浮動」同樣,屬於答不上來就會掛掉的前端基礎知識了。css
1.promise基本用法;前端
2.promise A+手動實現vue
3.promise 使用中的哪些坑 promise.all 回調地獄等;面試
promise對象簡單的來講,有點相似於ajax,它能夠看作是一個裝有某個將來纔會結束的事件的容器。它有兩個特色:1,promise的狀態不受外部影響;2.promise的狀態一旦改變,就不會再變了。ajax
能夠先看下promise的結構編程
function Promise(executor){ var self = this self.status = 'pending' // Promise當前的狀態 self.data = undefined // Promise的值 self.onResolvedCallback = [] // Promise resolve時的回調函數集,由於在Promise結束以前有可能有多個回調添加到它上面 self.onRejectedCallback = [] // Promise reject時的回調函數集,由於在Promise結束以前有可能有多個回調添加到它上面 executor(resolve, reject) // 執行executor並傳入相應的參數 }
promise的結構簡單來講就是,它有個status表示三種狀態,pending(掛起),resolve(完成),reject(拒絕)。除此以外,它還有兩個回調方法onResolvedCallback 和 onRejectedCallback,分別對應resolve(完成) 和reject(拒絕)。json
假如前面面試官問你promise的概念和基本用法,你像我同樣提到了ajax的話,面試官極可能就會順口問一下ajax(畢竟也是前端應該掌握得基礎知識之一)。根據個人經驗來看,一線大廠的面試官這個時候極可能會要你用promise擼一個ajax出來,一來能夠考察你promise和ajax掌握得怎麼樣,二來能夠考察你的代碼能力。數組
1.1用promise來實現Ajaxpromise
const $ = (function(){ const ajax = function(url, async = false, type = 'GET'){ const promise = new Promise(function(resolve, reject){ const handler = function(){ if(this.readyState !== 4){ return; } if(this.status === 200){ resolve(this.response); }else{ reject(new Error(this.statusText)); } } const client = new XMLHttpRequest(); client.open(type, url); client.onreadystatechange = handler; client.responseType = 'json'; client.setRequestHeader("Accept", "application/json"); client.send(); }) return promise; } return { ajax }; })()
調用方式:數據結構
$.ajax("/posts.json").then(function(json) { console.log('Contents: ' + json); }, function(error) { console.error('出錯了', error); });
要注意的幾個點:1.then方法會返回一個新的promise,所以then方法應該寫到原型鏈上。2.promise 的返回值或者拋出的err 會有傳遞現象。
例如:
new Promise(resolve=>resolve(8)) .then() .catch() .then(function(value) { alert(value) }) // 根據promise的定義和調用方式,能夠先寫出promise的數據結構 function Promise(executor){ const _this = this; _this.status = 'pending'; _ths.data = undefined; _this.onRejectedCallback = []; _this.onResolvedCallback = []; function resolve(value){ if(_this.status === 'pending'){ _this.status = 'resolved'; _this.data = value; for(let i=0;i<_this.onResolvedCallback.length;i++){ _this.onResolvedCallback[i](value); } } } function reject(reason){ if(_this.status === 'pending'){ _this.status = 'rejected'; _this.data = reason; for(let i=0;i<_this.onResolvedCallback.length;i++){ _this.onRejectedCallback[i](reason); } } } try{ executor(resolve, reject); }catch (e){ reject(e) } } // then方法應該寫在原型鏈上 Promise.prototype.then = function(onResolved, onRejected){ const self = this; // 要判斷onResolved 和 onRejected是否是方法 onResolved = typeof onResolved === 'function' ? onResolved : function(value) { return value } onRejected = typeof onRejected === 'function' ? onRejected : function(reason) { return reason } if(self.status === 'resolved'){ return new Promise(function(resolve, reject){ try{ const resoult = onResolved([self.data](https://link.zhihu.com/?target=http%3A//self.data)); if( resoult instanceof Promise ){ // 若是返回的是新的promise,那麼用這個promise的痛恨方法 resoult.then(resolve, reject) } resolve(resoult) // 不然 直接講返回值做爲newPromise的結果 }.catch(e){ reject(e); } }); } // 此處與前一個if塊的邏輯幾乎相同,區別在於所調用的是onRejected函數,就再也不作過多解釋 if (self.status === 'rejected') { return new Promise(function(resolve, reject) { try { var resoult = onRejected([self.data](https://link.zhihu.com/?target=http%3A//self.data)) if (resoult instanceof Promise) { resoult.then(resolve, reject) } } catch (e) { reject(e) } }) } if(self.status === 'pending'){ return new Promise(function(){}); } if (self.status === 'pending') { // 若是當前的Promise還處於pending狀態,咱們並不能肯定調用onResolved仍是onRejected, // 只能等到Promise的狀態肯定後,才能確實如何處理。 // 因此咱們須要把咱們的**兩種狀況**的處理邏輯作爲callback放入promise1(此處即this/self)的回調數組裏 // 邏輯自己跟第一個if塊內的幾乎一致,此處不作過多解釋 return Promise(function(resolve, reject) { self.onResolvedCallback.push(function(value) { try { var x = onResolved([self.data](https://link.zhihu.com/?target=http%3A//self.data)) if (x instanceof Promise) { x.then(resolve, reject) } } catch (e) { reject(e) } }) self.onRejectedCallback.push(function(reason) { try { var x = onRejected([self.data](https://link.zhihu.com/?target=http%3A//self.data)) if (x instanceof Promise) { x.then(resolve, reject) } } catch (e) { reject(e) } }) }) } }
在面試的時候,若是能答出來promise的使用中可能會出現什麼坑,已經如何避免這些坑。相信可以給面試官一個好印象,尤爲是面試2年工做經驗的崗位的時候,經過這些就能很好的和培訓班畢業的假簡歷區分開來。並且這些注意的點也是我本人在項目中實實在在踩過的坑。
注意點1,不要刻意爲了美化代碼而避免使用嵌套結構。
不少promise的科普教程裏,做者都會強調,爲了代碼的間接性,儘可能不要使用嵌套結構。ES7裏還爲了處理這個事情,專門設計了async await語法。但不少新手,再沒有充分理解業務的前提下,盲目的爲了美化代碼,爲了不「回調地獄」,常常會形成很大的問題。
1.1promis.all 的坑
設想一個場景,好比咱們寫的一個表單組件。以下圖所示:
這裏有三個選項組件,每一個組件都對應一個字典。當時組裏的一個實習生,爲了簡潔美化代碼,在這樣一個組件裏使用的Promise.all()。
相似於這樣:
fetchData1 = function (){ // 請求組件1的字典}; fetchData2 = function (){ // 請求組件2的字典}; fetchData3 = function (){ // 請求組件3的字典}; Promise.all([fetchData1, fetchData2, fetchData3 ]);
當時看這段代碼,並無發現什麼問題。結果後來一輩子產環境,問題就出來,控件1硬是獲取不到字典項,可是獲取組件1字典的接口,怎麼查都是好的。最後只能從新看一遍組件的源代碼,才發現了問題。原理是控件2的接口出現了問題,致使於整個Promise.all請求報錯。
因此,在使用promise.all的時候要注意:業務上沒有必然關聯的請求好比聯動組件這種,必定不要使用promise.all。
2.回調地獄並不可怕,不要盲目的使用async await
下面是比較常見的前端代碼:
asycn ()=>{ await 獲取訂單1的數據; await 獲取訂單2的數據; ...... }
當訂單2的數據與訂單1的數據直接沒有相互依賴的關係的時候。獲取訂單2的執行時間就多了一倍的訂單1的時間。一樣的道理,假如後面還有訂單3,訂單4,那浪費的時間就更多了。這也是會形成前端頁面卡頓的主要緣由。
面對這樣的常考題型,我以爲也要認真對待,由於面試中若是僅僅只是背答案,也極可能會掛掉。假如面試官看出來你在背答案,他只須要把相關的知識點都問一下,或者讓你手動實現一下,又或者問你在項目中遇到了什麼坑,你是怎麼處理的。準備不充分的面試者,一會兒就會露出馬腳。
因此小編爲你們準備了前端面試題資料,以前小編把前端面試問題的知識點整理成PDF文檔,方便本身查閱學習,如今免費分享給你們,小編新建了一個前端學習圈,歡迎你們加入學習聊天哦~但願你們在裏面有所收穫也聊得開心!小夥伴們點擊這裏進羣玩並取資料哦!
最近和小夥伴聊天瞭解到面試問題問vue比較多些,小編把vue相關的面試資料一塊兒分享給你們,也是同樣點擊這裏免費獲取哦!
篇幅有限,小編沒有展現完,須要文章中出現的所有資料的,點擊獲取來源:前端面試題資料就好嘍,祝各位能在本身的人生找到屬於本身的職場生涯!