面试 | 商汤科技面试经历之Promise红绿灯的实现
说在前面
说实话,刚开始在听到这个面试题的时候,我是诧异的,红绿灯?这不是单片机、FPGA、F28335、PLC的实验吗?!
而且还要用Promise去写,当时我确实没思路,只好硬着头皮去写,下来再review的时候,才真正懂了Promise红绿灯的实现原理
下来我就由浅至深的分析Promise红绿灯的实现原理
下面我就不讲promise的原理和特点了,想具体看了解的可以看阮一峰老师的教程
主要说下红绿灯用到promise最核心的一点就是 “promise实例的状态变为Resolved,就会触发then方法绑定的回调函数”
我是在做这个demo途中才彻底理解了这句话的真正含义。
简单实现
用文字绿灯、黄灯、红灯来模拟表示红绿灯
function timeout(){
return new Promise(function(resolve,reject){
setTimeout(resolve,1000,"绿灯");
}
function timeout2(){
return new Promise(function(resolve,reject){
setTimeout(resolve,2000,"黄灯");
})
}
function timeout3(){
return new Promise(function(resolve,reject){
setTimeout(resolve,3000,"红灯");
})
}
(function restart(){
timeout().then((value)=>{
console.log(value);
})
timeout2().then((value)=>{
console.log(value);
})
timeout3().then((value)=>{
console.log(value);
restart();
})
})()
建立三个promise对象,分别用timeout1 timeout2 timeout3 包起来,promise对象里面含有定时器setTimeout,以连续的1000-》2000-》3000的时间表示每次灯亮的时间的为1秒
下面是实现的demo效果
这种实现有一个问题,如果设定绿灯是5000ms,黄灯是2000ms,红灯是3000ms,
则会出现先显示黄灯,后显示红灯,显示绿灯的同时也会同时显示黄灯,
因为第二轮绿灯的5000ms包含了黄灯的2000ms
这就不符合红绿灯的思想与逻辑
较复杂实现
针对上一个问题,所以有了第二种解决方案
function green(){
return new Promise(function(resolve,reject){
console.log("绿灯"+new Date().getSeconds())
resolve();
})
}
function yellow(){
return new Promise(function(resolve,reject){
console.log("黄灯"+new Date().getSeconds())
resolve();
})
}
function red(){
return new Promise(function(resolve,reject){
console.log("红灯"+new Date().getSeconds())
resolve();
})
}
function ms_5000(){
return new Promise(function(resolve,reject){
setTimeout(resolve,5000)
})
}
function ms_3000(){
return new Promise(function(resolve,reject){
setTimeout(resolve,3000)
})
}
function ms_2000(){
return new Promise(function(resolve,reject){
setTimeout(resolve,2000)
})
}
(function restart(){
green()
.then(ms_5000) //绿灯显示5s转红灯
.then(yellow)
.then(ms_3000) //黄灯显示3s转红灯
.then(red)
.then(ms_2000) //红灯显示2s转绿灯
.then(arguments.callee)
})()
建立三个promise对象 分别用green yellow red 函数包起来,并返回promise对象的resolve,promise对象的状态变成Resolved 也就是说return了reslove就可以可以触发then方法绑定的回调函数
又建立了三个定时器,用于延时,三个定时器中用到了resolve函数,resolve是js引擎自带的函数,也表示promise的状态变成了Resolved,可以触发then方法绑定的回调函数。
实现的demo如下,demo的数字是时间戳,当前的秒数,绿灯55 黄灯0 表示绿灯执行5秒后转到黄灯,下面的同理
但是这样做还是有点麻烦,代码复用率低,要建立3个promise对象,3个定时器,无疑是消耗内存的。
倒数第2行 arguments.callee的含义下面也会解释。
较复杂实现(理理思路)
function green(){
return new Promise(function(resolve,reject){
console.log("绿灯当前秒数"+new Date().getSeconds())
resolve();
})
}
(function restart(){
green().then(function(){
return new Promise(function(resolve,reject){
setTimeout(resolve,5000);
})
}).then(function(){
return new Promise(function(resolve,reject){
console.log("黄灯当前秒数" + new Date().getSeconds())
resolve();
})
}).then(function(){
return new Promise(function(resolve,reject){
setTimeout(resolve,3000);
})
}).then(function(){
return new Promise(function(resolve,reject){
console.log("绿灯当前秒数"+ new Date().getSeconds())
resolve();
})
}).then(function(){
return new Promise(function(resolve,reject){
setTimeout(resolve,2000)
})
}).then(arguments.callee);
})()
上述的代码功能和第2点相同,只是为了理理思路,体现出promise的状态变成Resolved时,可以触发then方法绑定的回调函数
就像上述代码所示,执行红绿灯的显示,每次都会返回resolve,或者定时器也会使用resolve函数,表示promise的状态确实变成Resolved了。promise有三种状态pending fullfilled rejected ,pending到fulfilled表示的就是Resolved。
demo如下
完美实现(实现架构)
正如上面所说,上述的方法要建立3个promise对象,代码复用率低,那有没有更加严(gao)格(duan)的的方法,答案是有的,但是在看写代码前需要理理思路,分析一下代码的架构如何去写
function green2yellow2red(){
return function(){
// someCode
return new Promise(function(){
// someCode
})
}
}
var green = green2red2yellow(setTimeout).bind(null, 3000);
var yellow = green2red2yellow(setTimeout).bind(null, 4000);
var red = green2red2yellow(setTimeout).bind(null, 5000); (function(){
// IIFE
green()
})()
上述代码使用一个promise对象,用green2yellow2red函数包起来,有两个return,第一个return是为了给第二个return的promise对象bind延迟时间,bind绑定的参数可以通过arguments访问到,必须是第一个return的函数中的arguments,arguments是什么下面也会讲。
下面打印好多参数,下面我详细解释一下他们的区别
function green2red2yellow(){
console.log(arguments)
// [ƒ, callee: ƒ, Symbol(Symbol.iterator): ƒ]
// [ƒ, callee: ƒ, Symbol(Symbol.iterator): ƒ]
// [ƒ, callee: ƒ, Symbol(Symbol.iterator): ƒ]
console.log(this);
// Window {stop: ƒ, open: ƒ, alert: ƒ, confirm: ƒ, prompt: ƒ, …}
// Window {stop: ƒ, open: ƒ, alert: ƒ, confirm: ƒ, prompt: ƒ, …}
// Window {stop: ƒ, open: ƒ, alert: ƒ, confirm: ƒ, prompt: ƒ, …}
return function(){
console.log(arguments)
// [3000, callee: ƒ, Symbol(Symbol.iterator): ƒ]
console.log(this);
// Window {stop: ƒ, open: ƒ, alert: ƒ, confirm: ƒ, prompt: ƒ, …}
console.log(arguments.callee.length)
// 形参的个数
console.log(arguments.length)
// 实参的个数
var arr = [];
var arr2 =[].slice.call(arguments); //把arguments类数组转成真数组
console.log(arguments[0]) //3000 type是Number
console.log(arr.push(arguments)) //返回1表示当前代码执行结果为真
console.log(arr); //[Arguments(1)]
console.log(arr2) // [3000] type是Array
return new Promise(function(){
console.log(arguments)
// (2)[ƒ, ƒ, callee: ƒ, Symbol(Symbol.iterator): ƒ]
// 实参的类数组
})
}
}
var green = green2red2yellow(setTimeout).bind(null, 3000);
var yellow = green2red2yellow(setTimeout).bind(null, 4000);
var red = green2red2yellow(setTimeout).bind(null, 5000); (function(){
// IIFE
green()
})()
//测试代码段
var promise = new Promise(function(){
console.log(arguments)
// (2)[ƒ, ƒ, callee: ƒ, Symbol(Symbol.iterator): ƒ]
})
在green2red2yellow函数中直接console.log(arguments),打印出来三个数组,是因为实例了三次promise对象,分别是green,yellow,red,三次都指向同一个对象,所以打印了三次。
在green2red2yellow函数中的第一个return中console.log(arguments),只打印在IIFE中执行的的promise对象,就是green对象
在green2red2yellow函数中的第二个return中console.log(arguments),显示结果前面有一个2表示,实参的个数是2
在测试代码段中测试了一下,确实是。
arguments:以类数组的方式存放着当前对象的实参。green2red2yellow函数中访问是green2red2yellow这个函数对象,green2red2yellow函数中第一个return中访问是return的function bind了参数的的对象,green2red2yellow函数中第二个return是promise对象
arguments.callee:正在执行的这个函数的引用
arguments.callee.length:当前对象形参的个数
arguments.length:当前对象实参的个数
如何把arguments这个类数组转换成数组呢:[ ].slice.call(arguments) 这是最稳妥的方法
下面的使用两种方式把arguments转成数组及两者的区别
var arr = [];
var arr2 =[].slice.call(arguments); //把arguments类数组转成真数组
console.log(arguments[0]) //3000 type是Number
console.log(arr.push(arguments)) //返回1表示当前代码执行结果为真
console.log(arr); //[Arguments(1)]
console.log(arr2) // [3000] type是Array
由此可知 [ ].slice.call(arguments)是最稳妥的方式
完美实现
下面写出我觉得最完美的的实现方式
html:
<ul id="traffic" class="">
<li id="green"></li>
<li id="yellow"></li>
<li id="red"></li>
</ul>
css:
/*垂直居中*/
ul {position: absolute;width: 200px;height: 200px;top: 50%;left: 50%;transform: translate(-50%,-50%);}
/*画3个圆代表红绿灯*/
ul >li {width: 40px;height: 40px;border-radius:50%;opacity: 0.2;display: inline-block;}
/*执行时改变透明度*/
ul.red >#red, ul.green >#green,ul.yellow >#yellow{opacity: 1.0;}
/*红绿灯的三个颜色*/
#red {background: red;}
#yellow {background: yellow;}
#green {background: green;}
JS:
function green2red2yellow(timer){
return function(){
var arr = [].slice.apply(arguments)
// var self = this;
return new Promise(function(resolve,reject){
arr.unshift(resolve)
timer.apply(self,arr);
})
}
}
var green = green2red2yellow(setTimeout).bind(null, 3000);
var yellow = green2red2yellow(setTimeout).bind(null, 4000);
var red = green2red2yellow(setTimeout).bind(null, 5000);
var traffic = document.getElementById("traffic");
(function restart(){
'use strict' //严格模式
console.log("绿灯"+new Date().getSeconds()) //绿灯执行三秒
traffic.className = 'green'; green()
.then(function(){
console.log("黄灯"+new Date().getSeconds()) //黄灯执行四秒
traffic.className = 'yellow';
return yellow();
})
.then(function(){
console.log("红灯"+new Date().getSeconds()) //红灯执行五秒
traffic.className = 'red';
return red();
}).then(function(){
restart()
})
})();
1、var arr = [].slice.apply(arguments)
表示把arguments转成数组 2、arr.unshift(resolve)
unshift或shift 在数组首项插入某值或删除首项 push pop 是在数组尾部操作
3、timer.apply(self,arr);
timer是形参,引用了定时器setTimeout,apply是改变this的指向并可以数组的形式传入参数作为
定时器执行的形参,定时器this的指向为self
self就是this就是window,等价于 timer(arr[0],arr[1]);
4、'use strict'
严格模式,在严格模式下 arguments.callee(正在执行的这个函数的引用)无效 5、restart()
递归 6、promise的状态变成Resolved,就会触发then绑定的回调函数,
所以每次then都是return一个promsise对象,因为在promise对象中状态变成了Resolved 下面是实现的demo
注意:立即执行函数的script要写入body里面,否则会显示dom操作获得元素为null,我刚踩到这个坑了...
总结
这个问题的解决让我重新认识了promise,又重新认识了arguments,又重新认识了JS的强大。
面试 | 商汤科技面试经历之Promise红绿灯的实现的更多相关文章
- 商汤科技汤晓鸥:其实不存在AI行业,唯一存在的是“AI+“行业
https://mp.weixin.qq.com/s/bU-TFh8lBAF5L0JrWEGgUQ 9 月 17 日,2018 世界人工智能大会在上海召开,在上午主论坛大会上,商汤科技联合创始人汤晓鸥 ...
- 计蒜客 第四场 C 商汤科技的行人检测(中等)平面几何好题
商汤科技近日推出的 SenseVideo 能够对视频监控中的对象进行识别与分析,包括行人检测等.在行人检测问题中,最重要的就是对行人移动的检测.由于往往是在视频监控数据中检测行人,我们将图像上的行人抽 ...
- 2019 计蒜之道 初赛 第一场 商汤AI园区的n个路口(中等) (树形dp)
北京市商汤科技开发有限公司建立了新的 AI 人工智能产业园,这个产业园区里有 nn 个路口,由 n - 1n−1 条道路连通.第 ii 条道路连接路口 u_iui 和 v_ivi. 每个路口都布有 ...
- 旷视向左、商汤向右,AI一哥之名将落谁家
编辑 | 于斌 出品 | 于见(mpyujian) AI风口历经多年洗礼之后,真正意义上的AI第一股终于要来了. 相比于聚焦在语音识别技术上的科大讯飞.立足互联网产业的百度.发力人形机器人领域的优必选 ...
- 回客科技 面试的 实现ioc 容器用到的技术,简述BeanFactory的实现原理,大搜车面试的 spring 怎么实现的依赖注入(DI)
前言:这几天的面试,感觉自己对spring 的整个掌握还是很薄弱.所以需要继续加强. 这里说明一下spring的这几个面试题,但是实际的感觉还是不对的,这种问题我认为需要真正读了spring的源码后说 ...
- 面试北京XX科技总结
1 面试时间与地点 面试时间:2019年1月17号,面试地点:北京. 2 公司概况 开发的产品是集团内部使用,开发的语言ts脚本语言.目前开发团队15人 ...
- [Interview]读懂面试问题,在面试官面前变被动为主动
面试是供需双方心理的较量,作为求职者来说,了解对方问题的内涵,做到“明明白白他的心”,就能变被动为主动.因此,读懂面试问题,掌握面试考官的提问的目的,有准备.有针对性地回答,对提高应聘的成功率是有很大 ...
- 如何准备Java面试?如何把面试官的提问引导到自己准备好的范围内?
Java能力和面试能力,这是两个方面的技能,可以这样说,如果不准备,一些大神或许也能通过面试,但能力和工资有可能被低估.再仔细分析下原因,面试中问的问题,虽然在职位介绍里已经给出了范围,但针对每个点, ...
- Android开发面试经——6.常见面试官提问Android题②(更新中...)
版权声明:本文为寻梦-finddreams原创文章,请关注:http://blog.csdn.net/finddreams 关注finddreams博客:http://blog.csdn.net/fi ...
随机推荐
- xdu_1064:Desolator in RA2
问题转化为,单个面积*2-交面积.下面求交面积.把直角坐标系中全部转90°,每个方块的坐标都做相应变化,这样会发现新的坐标系中空出了一部分方块,找规律发现,若求交矩形包含的方框数,其中恰好一半是前面空 ...
- (转)Java.lang.reflect.Method invoke方法 实例
背景:今天在项目中用到Method 的invoke方法,但是并不理解,查完才知道,原来如此! import java.lang.reflect.Method; /** * Java.lang.refl ...
- (转)memcached学习笔记1(windows 7 64bit 环境下安装memcached)
windows 7 64bit 环境下安装memcached 1.下载后解压到D:\memcached(下载地址:memcached-win64下载地址) 2.安装到windows服务,打开cmd命令 ...
- 【Canvas】canva实例-星空、日出的效果
一.描述 模仿星空后黎明到来,日出的场景 二.代码 <!DOCTYPE html> <html> <head> <title></title> ...
- 【Apache】 VirtualHost配置
主要配置两点: 1)配置vhost ,可单独建文件,也可直接在http.conf添加内容 如果单独建文件,查看http.conf 中Include 配置文件的路径,并在对应路径新建http_vhost ...
- 如何重置密码 oracle sys和system
有时候我们会忘记oracle sys和system的密码,不用担心,通过sqlplus即可修改密码.只能修改,不能找回. 方法如下: 1.打开 cmd界面,输入sqlplus /nolog 显示结果如 ...
- R语言 write.xlsx() 写入同一excel,及同一sheet注意
write.xlsx(x, file, sheetName="Sheet1", col.names=TRUE, row.names=TRUE, append=FALSE, show ...
- 分布式web架构中对session同步的常用处理方法以及优缺点
写在前面 最近在读一本来自淘宝技术团队大牛的书,名字叫<大型网站系统与Java中间件实践>.开篇的章节详细地介绍了一个网站架构由小变大不断演进的过程,其中从单机架构升级到集群架构的过程中着 ...
- mybatis 详解(三)------入门实例(基于注解)
1.创建MySQL数据库:mybatisDemo和表:user 详情参考:mybatis 详解(二)------入门实例(基于XML) 一致 2.建立一个Java工程,并导入相应的jar包,具体目录如 ...
- Java常用文件操作-2
上篇文章记录了常用的文件操作,这里记录下通过SSH服务器操作Linux服务器的指定路径下的文件. 这里用到了第三方jar包 jsch-0.1.53.jar, jsch-api 1.删除服务器上指定路径 ...