寫在前面 這個文章,展現的是一個實現Promise的思路,以及如何發現和處理問題的情境。 從現有的Promise分析 如果我們想要自己實現一個簡單的 ,那現有規範規定的 肯定是我們最好的參照。 我們先看下 怎麼使用: 來看下返回的 是什麼,以及它的結構是怎麼樣的: 再進行一些具體操作 從Promis ...
寫在前面
這個文章,展現的是一個實現Promise的思路,以及如何發現和處理問題的情境。
從現有的Promise分析
如果我們想要自己實現一個簡單的Promise
,那現有規範規定的Promise
肯定是我們最好的參照。
我們先看下Promise
怎麼使用:
var promise1 = new Promise(function(resolve, reject){
// 成功後的TODO
resolve(value);
// 失敗後的TODO
reject(err);
})
來看下返回的promise1
是什麼,以及它的結構是怎麼樣的:
再進行一些具體操作
var promise1 = new Promise(function(resolve, reject) {
resolve('zqz')
})
promise1.then(function(result) {
console.log(result)
}).catch(function(err){
console.log(err)
})
// => 'zqz'
var promise1 = new Promise(function(resolve, reject) {
reject('出現異常')
})
promise1.then(function(result) {
console.log(result)
}).catch(function(err){
console.log(err)
})
// => '出現異常'
從Promise的 使用方式上 和 實例 可以看到哪些東西:
- Promise是一個構造函數
- Promise包含一個參數,這個參數類型是一個_匿名函數_
- 匿名函數包括2個形參,分別是
reject
和resolve
- 這兩個形參類型是 函數 ,且
reject
和resolve
都有一個參數, 參數類型不限定 - 實例 是個 Promise
- 實例的 原型 上掛載了 2個方法,分別是
then
和catch
,同時then可以有多個,所以需要一個回掉函數隊列 - 實例上 有2個屬性,分別是
PromiseStatus
和PromiseValue
- Promise根據定義 PromiseStatus 需要有 3種狀態,分別是
pending
,fulfilled
,rejected
根據上面的分析情況,我們先簡單的來構造一個雛形。
function Promise(fn) {
this.PromiseStatus = 'pending';
this.PromiseValue = '';
this.resolvedCb = [];
this.rejectedCb = [];
var self = this;
var resolve = function (result) {
// 判斷狀態
if (self.PromiseStatus === 'pending') {
self.PromiseStatus = 'resolved';
self.PromiseValue = result;
// resolvedCb 隊列依次執行
for (var i = 0;i < self.resolvedCb.length; i++) {
self.resolvedCb[i](result)
}
}
}
var reject = function (err) {
// 判斷狀態
if (self.PromiseStatus === 'pending') {
self.PromiseStatus = 'rejected';
self.PromiseValue = err;
// rejectedCb 隊列依次執行
for (var i = 0;i < self.rejectedCb.length; i++) {
self.rejectedCb[i](result)
}
}
}
// 錯誤處理 -> rejected
try {
fn(resolve, reject)
} catch(e) {
reject(e)
}
}
當然這還不夠,因為重要的兩個功能then
和catch
還沒有實現。
從現有的 then 分析
分析下then
的使用
promise1.then(function(value){
// todo
return value;
})
.then(function(value1){
// todo
return value1;
})
.then(function(value2){
// todo
return value2;
})
- promise1 返回的值 需要塞到第一個then中函數的value上
- 鏈式調用,多次調用
- then返回的是一個新的Promise
- then可以接受2個函數作為參數,一個是成功函數,一個是失敗函數
return
的值 直接作為下個then
中匿名函數的入參
根據Promise返回的實例,我們可看出來then
是掛載在 Promise 的原型鏈上。
我們先實現一個大體的框架:
Promise.prototype.then = function (handleSuccess, handleFail) {
var self = this;
var PromiseStatus = this.PromiseStatus;
if(typeof handleSuccess === 'function') {
handleSuccess = handleSuccess;
} else {
handleSuccess = function (result) {}
}
if(typeof handleFail === 'function') {
handleFail = handleFail;
} else {
handleFail = function (err) {}
}
if(PromiseStatus === 'pending') {
return new Promise(function(resolve, reject) {
self.resolvedCb.push(handleSuccess);
self.rejectedCb.push(handleFail);
})
}
if(PromiseStatus === 'resolved') {
return new Promise(function(resolve, reject) {
var result = handleSuccess(self.PromiseValue);
resolve(result);
})
}
if(PromiseStatus === 'rejected') {
return new Promise(function(resolve, reject) {
var result = handleFail(self.PromiseValue);
reject(result);
})
}
}
我們先用一下,看下是否符合期望
方式一(無非同步操作):
function promise1() {
return new Promise(function(resolve, reject){
console.log('執行promise1')
resolve('zqz');
})
}
promise1().then(function(result){
console.log('執行1', 'result:'+result)
return result + '11';
})
.then(function(result){
console.log('執行2', 'result:'+result)
return result + '22';
})
// => 執行promise1
// => 執行1 result:zqz
// => 執行2 result:zqz11
// => Promise {PromiseStatus: "resolved", PromiseValue: "zqz1122", resolvedCb: Array(0), rejectedCb: Array(0)}
這樣使用沒有問題!
方式二(有非同步操作):
function promise1() {
return new Promise(function(resolve, reject){
// 非同步操作
setTimeout(function(){
console.log('執行promise1')
resolve('zqz');
},1000)
})
}
promise1().then(function(result){
console.log('執行1', 'result:'+result)
return result + '11';
})
.then(function(result){
console.log('執行2', 'result:'+result)
return result + '22';
})
// => 執行promise1
// => 執行1 result:zqz
一旦出現非同步操作,就有問題!很明顯,Promise的主要作用就是控制非同步操作的執行順序。
肯定是哪裡有問題,我們來分析一下,非同步的時候 有哪些 不同
- 當有非同步操作(xhr,setTimeout等)的時候,這時候
PromiseStatus
是pending
狀態
在來看下我們在pending
時候的處理
...
// 非同步時
if(PromiseStatus === 'pending') {
return new Promise(function(resolve, reject) {
// 這裡只是將函數塞入隊列,然後就沒有然後來。。。這是有問題的
self.resolvedCb.push(handleSuccess);
self.rejectedCb.push(handleFail);
})
}
...
這時候我們的兩個數組:resolvedCb
和rejectedCb
就發揮作用了,由於我們不知道非同步什麼時候結束,但是我們可以根據他們定義的先後順序註入到 隊列
中,然後根據 順序
依次執行,這樣也就保證了非同步操作的執行順序。
if(PromiseStatus === 'pending') {
return new Promise(function(resolve, reject) {
// 一個個的塞入隊列
self.resolvedCb.push(function(result) {
var res = handleSuccess(self.PromiseValue);
resolve(res);
})
self.rejectedCb.push(function(err) {
var er = handleFail(self.PromiseValue);
reject(er);
})
})
}
這時候我們用多個非同步操作
來測試一下
function async1() {
return new Promise(function(resolve, reject){
// 非同步操作
setTimeout(function(){
console.log('執行async1')
resolve('zqz1');
},3000)
})
}
function async2() {
return new Promise(function(resolve, reject){
// 非同步操作
setTimeout(function(){
console.log('執行async2')
resolve('zqz2');
},1000)
})
}
function async3() {
return new Promise(function(resolve, reject){
// 非同步操作
setTimeout(function(){
console.log('執行async3')
resolve('zqz3');
},2000)
})
}
// return 一個新的promise
async1().then(function(result){
console.log('result = ' + result)
return async2();
}).then(function(result){
console.log('result = ' + result)
return async3();
}).then(function(result){
console.log('result = ' + result)
return result;
})
// => Promise {PromiseStatus: "pending", PromiseValue: "", resolvedCb: Array(0), rejectedCb: Array(0)}
// => 執行async1
// => result = zqz1
// => result = [object Object]
// => result = [object Object]
// => 執行async2
// => 執行async3
這裡有兩個問題:
- 返回promise的時候,執行順序有問題
- 返回promise的時候,無法進行值的傳遞
我們再來分析下,著重看下下麵這塊代碼
...
if(PromiseStatus === 'pending') {
return new Promise(function(resolve, reject) {
self.resolvedCb.push(function(result) {
// 這裡返回的res有可能是promise,但是我們沒有做處理
var res = handleSuccess(self.PromiseValue);
resolve(res);
})
self.rejectedCb.push(function(err) {
// 這裡返回的res有可能是promise,但是我們沒有做處理
var er = handleFail(self.PromiseValue);
reject(er);
})
})
}
...
因為我們返回的是Promise,由於我們沒有做處理,導致無法正確的獲取到值。
...
if(PromiseStatus === 'pending') {
return new Promise(function(resolve, reject) {
self.resolvedCb.push(function(result) {
var res = handleSuccess(self.PromiseValue);
if (res instanceof Promise) {
res.then(resolve, reject);
} else {
resolve(res);
}
})
self.rejectedCb.push(function(err) {
var er = handleFail(self.PromiseValue);
if (er instanceof Promise) {
er.then(resolve, reject);
} else {
reject(er);
}
})
})
}
...
如果返回的是一個Promise,就繼續塞入到then裡面。
在執行一下:
async1().then(function(result){
console.log('result = ' + result)
return async2();
}).then(function(result){
console.log('result = ' + result)
return async3();
}).then(function(result){
console.log('result = ' + result)
return result;
})
// => Promise {PromiseStatus: "pending", PromiseValue: "", resolvedCb: Array(0), rejectedCb: Array(0)}
// => 執行async1
// => result = zqz1
// => 執行async2
// => result = zqz2
// => 執行async3
// => result = zqz3
最後一個簡單完整的 then:
Promise.prototype.then = function (handleSuccess, handleFail) {
var self = this;
var PromiseStatus = this.PromiseStatus;
if(typeof handleSuccess === 'function') {
handleSuccess = handleSuccess;
} else {
handleSuccess = function (result) {}
}
if(typeof handleFail === 'function') {
handleFail = handleFail;
} else {
handleFail = function (err) {}
}
if(PromiseStatus === 'pending') {
return new Promise(function(resolve, reject) {
self.resolvedCb.push(function(result) {
var res = handleSuccess(self.PromiseValue);
if (res instanceof Promise) {
res.then(resolve, reject);
} else {
resolve(er);
}
})
self.rejectedCb.push(function(err) {
var er = handleFail(self.PromiseValue);
if (er instanceof Promise) {
er.then(resolve, reject);
} else {
reject(er);
}
})
})
}
if(PromiseStatus === 'resolved') {
return new Promise(function(resolve, reject) {
var result = handleSuccess(self.PromiseValue);
resolve(result);
})
}
if(PromiseStatus === 'rejected') {
return new Promise(function(resolve, reject) {
var result = handleFail(self.PromiseValue);
reject(result);
})
}
}