在淘天的一面中面试官出了这样一道题

1
2
3
4
5
6
7
8
9
10
11
12
13
/*
实现Monkey, 写个Monkey函数,返回的对象提供eat和sleep两个函数,支持链式调用。具体调用方式如下所示:
Monkey('Alan').eat('Banana').sleep(4).eat('Apple').sleep(5).eat('Pear')

代码执行后输出:

my name is Alan
I eat Banana
// 等待 4 s
I eat Apple
// 等待 5 s
I eat Pear
*/

我是这样写的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function Monkey(name){
console.log(`my name is ${name}`);
function temp(){
}
temp.prototype.eat= (food)=>{
console.log(`I eat ${food}`);
return new temp();
}
temp.prototype.sleep = (timeout)=>{
let start = + new Date();
while(+new Date() - start < timeout*1000){

}
return new temp();
}
return new temp();
}

// console.log(Monkey('Alan').eat('Banana').sleep(1))

Monkey('Alan').eat('Banana').sleep(4).eat('Apple').sleep(5).eat('Pear')

我不知道是不是这道题写的有问题导致一面挂了,但是我事后搜了一下这道题,可能面试官希望看到的是下面的写法吧(考察事件循环)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
function Monkey(name){
class Monkey{
constructor(){
console.log(`my name is ${name}`);
this.tasks = [];
setTimeout(()=>{
this.next();
})
}
next(){
console.log(this.tasks.length);
let fn = this.tasks.shift();
fn && fn();
}
eat(food){
this.tasks.push(()=>{
console.log(`I eat ${food}`);
this.next();
})
return this;
}
sleep(timeout){
this.tasks.push(()=>{
setTimeout(()=>{
this.next();
},timeout*1000);
});
// this.next();
return this;
}
}

return new Monkey();
}

Monkey('Alan').eat('Banana').sleep(4).eat('Apple').sleep(5).eat('Pear')

我在next方法中添加了打印当前队列任务数量,打印结果如下:

1
2
3
4
5
6
7
8
9
10
my name is Alan
5
I eat Banana
4
3
I eat Apple
2
1
I eat Pear
0

可以看到,链式调用中要等到每次函数结果返回后才开始执行setTimeout任务(因为第一次打印的数量是5,那就是说开始执行队列中的方法时,所有函数已经调用完毕,上面中比较容易写错的就是打印放在推入的函数外,这样就会造成打印完结果然后等待所需时间程序结束)。我的理解就是事件循环机制是不管你写的语句是不是在一个函数块中的,也就是说js执行的顺序就是按照顺序执行完所有函数的同步语句,然后按照顺序执行完所有函数的异步语句(分为微任务和宏任务)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
(function(){
console.log(1);
setTimeout(function(){
console.log(2);
});
})();
(function(){
console.log(3);
new Promise(function(resolve){
console.log(4);
resolve();
}).then(function(){
console.log(5);
});
})();

(function(){
console.log(6);
setTimeout(function(){
console.log(7);
});
new Promise(function(resolve){
console.log(8);
resolve();
}).then(function(){
console.log(9);
});
})();

理解了以后可以看到上述的输出为:

1
2
3
4
5
6
7
8
1
3
4
6
8
5
9
2