promise是用來解決Js中的異步問題的,js中所有的異步可從callback → promise → generator + co = async + await
其實所有的都是callback的變相處理,只是后者慢慢變的越來越優雅和強壯可擴展。
那么如何實現promise呢?先觀察一下promise的樣子
let a = new Promise((resolve,reject)=>{ // dosomething
resolve() })
無非是一個名稱叫Promise的對象,然后傳參一個函數 (resolve,reject)=>{resolve()}
promise的強大之處在於回調處理,promise.then(resolveFunticon,rejectFunction)
第一個函數是resolve的回調處理函數,第二個是reject
說到resovle,reject,那么還得再提一下pending,promise對象內部有以上三種狀態。默認是pending狀態
狀態的轉變需要我們手動自己去調用函數resolve()或者reject()
整理一下思路:new promise的時候callback作為傳參參數執行promise函數,callback在promise函數內部執行。callback里手動調用的resolve或者reject方法,那么promise對象里肯定是有這兩種方法的。
最關鍵的是記住狀態,然后再then的時候根據狀態去判斷到底是執行resolveFunticon還是rejectFunction。
function PromiseFn(callBack){ let self = this; self.resolveVal = undefined; self.rejectVal = undefined; self.status = 'pending'//默認是等待狀態
function resolve(val){ if(self.status === 'pending'){ self.resolveVal = val; self.status = 'resolve' } } function reject(val){ if(self.status === 'pending'){ self.rejectVal = val; self.status = 'reject' } }
//這里要注意,try catch若是寫在 let self = this 會報錯,let存在暫時死區,沒有常規的變量提升。必須先申明后使用
// callback執行,調用resolve函數。
try{ callBack(resolve,reject)//callback進來自動執行 }catch(e){ reject()//若是有報錯,則直接變為reject狀態 } }
上面關於let的深入可以去看這篇博客:我用了兩個月的時候才理解let
可能會被callback給弄得有點懵,其實把它當做函數fn就行,fn在promise內部執行,然后fn內部剛好有個resolve(),然后再去調用promise的這個resolve執行。
也就是這個原因,所以callback的參數必須是resolve和reject。
上面這一步,需要注意的是callback是立即執行的。下面這段會立即打印111
let a = new Promise((resolve,reject)=>{ console.log('111') })
console.log('456')
上面我們定義了 resolveVal 和 rejectVal,因為待會在promise調用then執行的時候,會作為參數傳參給resolveFunticon或者rejectFunction
所以promise對象內部必須記住這個參數。
下面一起看看promise.then吧,promise.then(resolveFunticon,rejectFunction)。在promise的原型鏈上有個then方法
PromiseFn.prototype.then = function(resolveFunction,rejectFunction){
let self = this;
if(self.status === 'resolve'){
resolveFunction(self.resolveVal)
}
if(self.status === 'reject'){
rejectFunction(self.rejectVal)
}
}
在then執行的時候,去判斷內部是啥狀態,然后執行對應的resolve或者reject回調函數。
然后運行試一試
let promiseA = new PromiseFn((resolve,reject)=>{
resolve('成功啦')
})
promiseA.then((resolveVal)=>{
console.log('resolve'+resolveVal)
},(rejectVal)=>{
console.log('reject'+rejectVal)
})
結果是:
但是這樣就結束了嗎?no,我們一般對Promise的使用場景可能是下面這樣的,需要考慮異步問題。
let promiseA = new PromiseFn((resolve,reject)=>{
setTimeout(function(){
resolve('成功啦')
},2000)
})
這里用settimeout代替一下,一般我們都是一個ajax請求,然后在請求到結果后去resolve.
上面的寫法顯然是無法滿足這種條件的,當去執行promise.then的時候,發現狀態還是pending,不做任何執行。
那我們怎么去做呢?發現狀態是pending,肯定是兩秒鍾之內啦,不妨先把promise.then兩個函數參數先在promise里面記下來,兩秒鍾后狀態變為resolve了,再執行函數。
function PromiseFn(callBack){
try{
callBack(resolve,reject)
}catch(e){
reject()
}
let self = this;
self.resolveVal = undefined;
self.rejectVal = undefined;
self.status = 'pending'//默認是等待狀態
self.keepResolveFn = []//
self.keepRejectFn = []//
function resolve(val){
if(self.status === 'pending'){
self.resolveVal = val;
self.status = 'resolve';
self.keepResolveFn.forEach(fn=>fn());
}
}
function reject(val){
if(self.status === 'pending'){
self.rejectVal = val;
self.status = 'reject';
self.keepRejectFn.forEach(fn=>fn());
}
}
//執行先記錄resolve和reject函數事件
}
PromiseFn.prototype.then = function(resolveFunction,rejectFunction){
let self = this;
if(self.status === 'resolve'){
resolveFunction(self.resolveVal)
}
if(self.status === 'reject'){
rejectFunction(self.rejectVal)
}
if(self.status === 'pending'){
self.keepResolveFn.push(()=>{
resolveFunction(self.resolveVal);
});
self.keepRejectFn.push(()=>{
rejectFunction(self.rejectVal)
});
}
}
let promiseA = new PromiseFn((resolve,reject)=>{
setTimeout(function(){
resolve('成功啦')
},2000)
})
promiseA.then((resolveVal)=>{
console.log('resolve'+resolveVal)
},(rejectVal)=>{
console.log('reject'+rejectVal)
})
結果是:延遲兩秒后,打印出 ‘resolve成功啦’。
最后多提一句:所有的then方法執行,也是通過return 一個新的promise對象來執行的。這樣才有鏈式回調,new promise.then().then().then()
var b = 10;
new Promise((resolve, reject) => {
setTimeout(() => {
b += 10;
resolve();
// reject();
}, 1000);
}).then(function() {
console.log(b);
return new Promise((resolve, reject) => {
setTimeout(() => {
b *= 2;
resolve();
}, 1000);
});
}).then(function() {
console.log(b);
return new Promise((resolve, reject) => {
setTimeout(() => {
b *= b;
resolve();
}, 1000);
});
}).then(function() {
console.log(b);
},function() {
console.log('失敗了');
});