前端面試之問到promise怎麼辦?

前言

Promise做爲面試中的經典考題,咱們必定要深入學習和理解它! Promise有什麼用呢?答:咱們拿它解決異步回調問題。Pomise是ES6裏面新增的一種異步編程的解決方案。如今這個promise在面試中感受就像「css清除浮動」同樣,屬於答不上來就會掛掉的前端基礎知識了。css

本文大綱:

1.promise基本用法;前端

2.promise A+手動實現vue

3.promise 使用中的哪些坑 promise.all 回調地獄等;面試

(一)promise基本用法

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(拒絕)。除此以外,它還有兩個回調方法onResolvedCallbackonRejectedCallback,分別對應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);

});

(二)手動實現一個 Promise/A

要注意的幾個點: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 的使用中應該要避免哪些坑

在面試的時候,若是能答出來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文檔,方便本身查閱學習,如今免費分享給你們,小編新建了一個前端學習圈,歡迎你們加入學習聊天哦~但願你們在裏面有所收穫也聊得開心!小夥伴們點擊這裏進羣玩並取資料哦!
image.png

image.png

最近和小夥伴聊天瞭解到面試問題問vue比較多些,小編把vue相關的面試資料一塊兒分享給你們,也是同樣點擊這裏免費獲取哦!
image.png

image.png

篇幅有限,小編沒有展現完,須要文章中出現的所有資料的,點擊獲取來源:前端面試題資料就好嘍,祝各位能在本身的人生找到屬於本身的職場生涯!

相關文章
相關標籤/搜索