關于Promise的了解
為什麼要發明promise?
在promise發明之前,使用回調函數和事件來實作異步,且發現存在弊端。promise是Javascript關于異步程式設計的一種解決方案,可以将promise了解為一個容器。
promise的原理(一)
我們通過一段代碼來了解:
class Promise {
callbacks = [];
constructor(fn) {
fn(this._resolve.bind(this));
}
then(onFulfilled) {
this.callbacks.push(onFulfilled);
}
_resolve(value) {
this.callbacks.forEach(fn => fn(value));
}
}
//Promise應用
let p = new Promise(resolve => {
setTimeout(() => {
console.log('done');
//console.log(resolve);
resolve('5秒');
}, 5000);
}).then((tip) => {
console.log(tip);
})
1.首先執行構造函數constructor,傳入函數(箭頭函數)作為參數,且傳入函數的參數是resolve函數。
2.執行then方法,将以下函數push進callbacks數組但還不執行。
(tip) => {
console.log(tip);
}
3.此時就開始執行以下的函數體:
setTimeout(() => {
console.log('done');
resolve('5秒')
}, 5000);
5秒後先輸出done,再執行resolve這個方法周遊callbacks數組,執行callbacks中的唯一一個函數,即then方法裡面的函數輸出value,且value=‘5秒’。
這個過程可以用如下圖進行解釋:
先将then方法中的函數加入回調系統資料庫(callbacks),再通過resolve執行回調函數。
promise的原理(二)
遇到then鍊式調用的情況,通過在Promise的then中添加return this。代碼如下:
class Promise {
callbacks = [];
constructor(fn) {
fn(this._resolve.bind(this));
}
then(onFulfilled) {
this.callbacks.push(onFulfilled);
return this;
}
_resolve(value) {
this.callbacks.forEach(fn => fn(value));
}
}
//Promise應用
let p = new Promise(resolve => {
setTimeout(() => {
console.log('done');
//console.log(resolve);
resolve('5秒');
}, 5000);
}).then((tip) => {
console.log('then1',tip);
}).then((tip) => {
console.log('then2',tip);
})
每次調用then将函數注冊回調,再傳回對象執行個體,再次調用then。
promise的原理(三)
但是有個問題,如果代碼如下時(由于沒有設定定時器,造成then沒有執行的情況):
class Promise {
callbacks = [];
constructor(fn) {
fn(this._resolve.bind(this));
}
then(onFulfilled) {
this.callbacks.push(onFulfilled);
}
_resolve(value) {
this.callbacks.forEach(fn => fn(value));
}
}
//Promise應用
let p = new Promise(resolve => {
console.log('done');
resolve('5秒');
}).then((tip) => {
console.log(tip);
})
結果是隻列印了done,沒有列印 5秒。原因是沒有執行then,使得callbacks空數組。
解決方案:
class Promise {
callbacks = [];
constructor(fn) {
fn(this._resolve.bind(this));
}
then(onFulfilled) {
this.callbacks.push(onFulfilled);
}
_resolve(value) {
setTimeout(()=>{this.callbacks.forEach(fn => fn(value))});
}
}
//Promise應用
let p = new Promise(resolve => {
console.log('done');
resolve('5秒');
}).then((tip) => {
console.log(tip);
})
在resolve中添加定時器,保證resolve執行時,then已經執行完。
promise的原理(四)
但是又有問題出現了,如果代碼如下:
let p = new Promise(resolve => {
console.log('同步執行');
resolve('同步執行');
}).then(tip => {
console.log('then1', tip);
}).then(tip => {
console.log('then2', tip);
});
//看這裡
setTimeout(() => {
p.then(tip => {
console.log('then3', tip);
})
});
由于resolve設定了定時器,導緻then3不能執行。
解決方案:
增加狀态(pending、fulfilled、rejected),且fulfilled、rejected隻能由pending轉化。
class Promise {
callbacks = [];
state = 'pending';//增加狀态
value = null;//儲存結果
constructor(fn) {
fn(this._resolve.bind(this));
}
then(onFulfilled) {
if (this.state === 'pending') {//在resolve之前,跟之前邏輯一樣,添加到callbacks中
this.callbacks.push(onFulfilled);
} else {//在resolve之後,直接執行回調,傳回結果了
onFulfilled(this.value);
}
return this;
}
_resolve(value) {
this.state = 'fulfilled';//改變狀态
this.value = value;//儲存結果
this.callbacks.forEach(fn => fn(value));
}
}
let p = new Promise(resolve => {
setTimeout(() => {
console.log('done');
resolve('5秒')
}, 5000);
}).then((tip) => {
console.log('then1',tip);
}).then((tip) => {
console.log('then2',tip);
});
setTimeout(() => {
p.then(tip => {
console.log('then3', tip);
})
});
參考: https://zhuanlan.zhihu.com/p/58428287