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相关的面试资料一块儿分享给你们,也是同样点击这里免费获取哦!
篇幅有限,小编没有展现完,须要文章中出现的所有资料的,点击获取来源:前端面试题资料就好喽,祝各位能在本身的人生找到属于本身的职场生涯!