閑逛的時候發(fā)現(xiàn)了一道LazyMan的前端面試題增热,感覺挺有意思稠氮,下面給出本人的結(jié)題思路洽瞬,以及關(guān)于promise的一些基礎(chǔ)
promise基礎(chǔ)
一棵帽、異步:現(xiàn)在與將來
1. 事件循環(huán)
<!--eventLoop 是一個用作隊列的數(shù)組-->
<!--(先進(jìn)先出)-->
var eventLoop = [];
var event;
<!--"永遠(yuǎn)"執(zhí)行-->
while(true){
<!--一次 tick-->
if(eventLoop.length>0){
<!--拿到隊列中的下一個事件-->
event = eventLoop.shift();
<!--現(xiàn)在妹卿,執(zhí)行下一個事件-->
try{
event();
}
catch(err){
reportError(err);
}
}
}
一定要清楚旺矾,setTimeout(..) 并沒有把你的回調(diào)函數(shù)掛在事件循環(huán)隊列中。它所做的是設(shè)定一個定時器夺克。當(dāng)定時器到時后箕宙,環(huán)境會把你的回調(diào)函數(shù)放在事件循環(huán)中,這樣铺纽,在未來某個時刻的tick會摘下并執(zhí)行這個回調(diào)函數(shù)
程序通常分成了很多小塊柬帕,在事件循環(huán)隊列中一個接一個地執(zhí)行,嚴(yán)格的說,和你的程序不直接相關(guān)的其他事件也可能會插入到隊列中陷寝。
2. js中的函數(shù)具有原子性
3. Promise
3.1 Promise 至多只能有一個決議值(完成或拒絕)
如果你沒有用任何值顯式?jīng)Q議锅很,那么這個值就是undefined,這是javaScript常見的處理方式。但是不管這個值是什么凤跑,無論當(dāng)前或者未來爆安,它都會被傳給所有注冊的(且適當(dāng)?shù)耐瓿苫蚓芙^)的回調(diào)。
還有一點(diǎn)需要清楚:如果使用多個參數(shù)調(diào)用resolve(..) 或者reject(..),第一個參數(shù)之后的所有參數(shù)都會被默默忽略仔引。如果傳遞多個值扔仓,你就必須要把它們封裝在單個值中傳遞,比如通過一個數(shù)組或者對象咖耘。
3.2 吞掉錯誤或異常
const p1 = new Promise(function (resolve,reject){
throw new Error("error");
resolve(1);
});
p1.then(function(value){
console.log("a",value);
},function(err){
console.log(err);
});
<!--輸出-->
<!--Error: error-->
<!-- at eval (lifting.js?f706:37)-->
<!-- at new Promise (<anonymous>)-->
<!-- at eval (lifting.js?f706:36)-->
<!-- at Object.<anonymous> (bundle.js:233)-->
<!-- at __webpack_require__ (bundle.js:20)-->
<!-- at Object.<anonymous> (bundle.js:83)-->
<!-- at __webpack_require__ (bundle.js:20)-->
<!-- at bundle.js:63-->
<!-- at bundle.js:66-->
<!--lifting.js?f706:54 a 2-->
<!--lifting.js?f706:51 Error: error-->
<!-- at eval (lifting.js?f706:37)-->
<!-- at new Promise (<anonymous>)-->
<!-- at eval (lifting.js?f706:36)-->
<!-- at Object.<anonymous> (bundle.js:233)-->
<!-- at __webpack_require__ (bundle.js:20)-->
<!-- at Object.<anonymous> (bundle.js:83)-->
<!-- at __webpack_require__ (bundle.js:20)-->
<!-- at bundle.js:63-->
<!-- at bundle.js:66-->
LazyMan 實現(xiàn)
1.代碼
function _LazyMan(name) {
this.name = name;
this.promises = [];
var mfunc = () =>{
console.log(`hello this is ${this.name}`);
var p = Promise.resolve();
return p
}
this.promises.push(mfunc);
var template = Promise.resolve();
setTimeout(() => {
this.promises.forEach(v =>{
template = template.then(v);
});
}, 0)
}
_LazyMan.prototype = {
sleep: function(time) {
var pfunc = function() {
var sp = new Promise(function(resolve, reject) {
setTimeout(() => {
console.log(`暫停${time}s!`);
resolve();
}, time * 1000);
});
return sp;
}
this.promises.push(pfunc);
return this;
},
eat: function(food) {
var epfunc = function() {
console.log(`正在吃 ${food}`);
var ep = Promise.resolve();
return ep;
}
this.promises.push(epfunc);
return this;
}
}
function LazyMan(name) {
return new _LazyMan(name);
}
LazyMan('yc').sleep(2).eat('shit').eat('peer').sleep(1).eat('shit').sleep('4').eat('banana');
如果不計較輸出形式翘簇,在構(gòu)造函數(shù)里面的for循環(huán)可以直接用 Promise.all()函數(shù)替代
2. 解析(大概意思可以看頂部手工圖)
2.1 箭頭函數(shù)
箭頭函數(shù)是ES6 新推出的形式,值得注意的是在setTimeOut的函數(shù)中用到了this儿倒,如果是傳統(tǒng)函數(shù)版保,這個this指向的是window,并不是_LazyMan夫否,但是如果是箭頭函數(shù)彻犁,this就是指向 _LazyMan
2.2 setTimeOut
setTimeOut函數(shù)并不會直接將回調(diào)函數(shù)放在事件循環(huán)隊列中,而是等到定時器時間到了之后才會將函數(shù)放到事件循環(huán)隊列中慷吊。正是基于setTimeOut函數(shù)的這種特性袖裕,所以在_LazyMan構(gòu)造函數(shù)中用setTimeout()去獲取promises中數(shù)組的數(shù)據(jù),此時promises的數(shù)據(jù)是調(diào)用鏈中的所有Promise對象溉瓶,如果不用setTimeout函數(shù)那么在構(gòu)造函數(shù)中急鳄,語句順序執(zhí)行,promises里的對象只有構(gòu)造函數(shù)中的一個Promise對象堰酿,鏈?zhǔn)秸{(diào)用函數(shù)中存入的Promise對象獲取不到疾宏。
2.3 鏈?zhǔn)秸{(diào)用
return this :返回整個對象,就能實現(xiàn)鏈?zhǔn)秸{(diào)用触创。