ES6——手把手實現一個簡單的Promise
阿新 • • 發佈:2019-01-08
想要實現一個Promise,首先當然得先知道這個東西是什麼,Promise物件是ES6給我們提供的一個解決非同步回撥的一個語法糖,簡單說就是一個容器,裡面儲存著某個未來才會結束的事件(通常是一個非同步操作)的結果。把非同步操作放入Promise物件中,等到Promise物件中的操作完成了,再繼續執行then裡面的操作。具體的使用就不說了,大概的使用方法如同下面。詳情可以看ES6
let p=new Promise(function (resolve,reject) { setTimeout(function () { resolve(2); },1000); }); p.then(function (n) { console.log(n); },function (n) { console.log(n); });
那麼接下來就來具體說說要怎麼去實現一個Promise物件,這裡我先用es5的語法實現一下,後面會附帶用es6的語法,也就是用到了“類”這個概念
首先按照es5建立物件的套路,先來個初始化
function Promise2(fn) { const _this = this; //方便this改變時,能夠繼續呼叫 this._queue = []; //promise關鍵,一個函式佇列,用於儲存為能夠開始執行的函式 this._succ_res = null; //用於存放成功時的引數 this._err_res = null; //用於存放失敗時的引數 this.status = ''; //Promise狀態 fn(); //傳入Promise的回撥函式 }
我們知道Promise傳進來的引數是一個函式,那麼最主要的事情就是要在Promise裡面去執行它。執行完之後呢,這個函式可能是執行成功的,也可能失敗,所以如下圖,我們可以看到有一個resolve和reject兩個方法。因此我們需要在傳進來的引數裡面設定兩個function。
function Promise2(fn) {
......
fn(function (...arg) {
//resolve
}, function (...arg) {
//reject
})
}
同時我們還有一個then的方法,這個時候就要搬出我們的原型了。那麼then裡面要做什麼呢?在then裡面,會被放入兩個函式,一個表示成功後執行的,一個表示失敗後執行的,所以then主要是要做下面的事
- 判斷當前Promise的狀態,Promise有3個狀態,pending、fulfilled和rejected。簡單講就是,Promise的函式還沒執行完、Promise的函式執行完了並且成功了、Promise執行完了但失敗了,這三種狀態。
- 如果未執行完:把then裡面要執行的函式放在等待佇列裡面
- 如果執行完且成功:執行then中表示成功的函式
- 如果執行完但失敗:執行then中表示失敗的函式
所以Promise的then如下
Promise2.prototype={
then:function (fn1, fn2) {
let _this=this;
if (_this.status == 'success') {
fn1(..._this._succ_res);
} else if (_this.status == 'error') {
fn2(..._this._succ_res);
} else {
_this._queue.push({fn1, fn2});
}
}
}
同樣是我們在原來的函式裡面,也要對狀態的改變進行設定。而狀態一旦確定下來也就不會再改變,那麼,也就要先判斷狀態是否已經發生改變
fn(function (...arg) {
//resolve
if (_this.status != 'error') { //判斷狀態是否已經變成失敗
_this.status = 'success'; //改變狀態
_this._succ_res = arg; //傳入resolve()中傳給then的引數
_this._queue.forEach(json => { //執行結束後,看看佇列裡是否有函式,有的話執行第一個,並傳入對應的引數
json.fn1(...arg);
});
}
}, function (...arg) {
//reject
if (_this.status != 'success') { //判斷狀態是否已經變成成功
_this.status = 'error'; //改變狀態
_this._err_res = arg; //傳入resolve()中傳給then的引數
_this._queue.forEach(json => { //執行結束後,看看佇列裡是否有函式,有的話執行第二個函式,並傳入對應的引數
json.fn2(...arg);
});
}
})
完整程式碼如下
function Promise2(fn) {
const _this = this;
this._queue = [];
this._succ_res = null;
this._err_res = null;
this.status = '';
fn(function (...arg) {
//resolve
if (_this.status != 'error') {
_this.status = 'success';
_this._succ_res = arg;
_this._queue.forEach(json => {
json.fn1(...arg);
});
}
}, function (...arg) {
//reject
if (_this.status != 'success') {
_this.status = 'error';
_this._err_res = arg;
_this._queue.forEach(json => {
json.fn2(...arg);
});
}
})
}
Promise2.prototype={
then:function (fn1, fn2) {
let _this=this;
if (_this.status == 'success') {
fn1(..._this._succ_res);
} else if (_this.status == 'error') {
fn2(..._this._succ_res);
} else {
_this._queue.push({fn1, fn2});
}
}
}
let p2=new Promise2(function (resolve,reject) {
setTimeout(function () {
resolve(22);
},1000);
setTimeout(function () {
reject(23);
},500);
});
p2.then(function (n) {
console.log(n);
}, function (n) {
console.log(n);
})
最後來總結一下
Promise之所以能夠把非同步的東西按照同步的形式去執行,無非因為兩點
- 當狀態未發生改變時,能夠將then中的函式暫時掛起
- 當狀態改變時,能夠呼叫其之前掛起的then佇列