前言
Promise 是 JavaScript 中用於非同步操作非常重要的一個建構函式,那麼它是怎麼實現的呢?
下面用原生 JavaScript 來實現一個簡易版的 Promise,用來實現以下程式碼。
function Promise(???){
???
return ???
}
var promise = new Promise(function(resolve, reject) {
setTimeout(() => {
resolve('hello');
}, 3000);
});
promise.then(
val => {
console.log('第一次成功', val);// 輸出 第一次成功 hello
},
val => {
console.log('第一次失敗', val);
}
);
複製程式碼
實現
用過 Promise 的朋友都知道,Promise 要接受一個函式 fn 並呼叫它,Promise 還應該有一個表示狀態( pending, resolved, rejected )的屬性值 status,一個 value 儲存 then 回撥時傳的值,還有一個 then 方法。其他方法暫時不考慮了。
function Promise(fn) {
//初始 status 為 peding
let status = 'pending';
//儲存 then 回撥值
let value;
//儲存 then 的兩個引數
let onResolvedCallback;
let onRejectedCallback;
//設定 fn 的 resolve 函式
function resolve(data) {
//將 status 變成 fulfilled
status = 'fulfilled';
//儲存回撥函式的值
value = data;
toDoThen(onResolvedCallback, onRejectedCallback);
}
//設定 fn 的 reject 函式
function reject(data) {
//將 status 變成 rejected
status = 'rejected';
//儲存回撥函式的值
value = data;
toDoThen(onResolvedCallback, onRejectedCallback);
}
//設定 resolve 或 reject 時的執行體
function toDoThen(onFulfill, onReject) {
//如果是 fulfilled 狀態,表示要執行 then 的第一個函式
if (status === 'fulfilled') {
onFulfill && onFulfill(value);
status = 'pending';
//如果是 rejected 狀態,表示要執行 then 的第二個函式
} else if (status === 'rejected') {
onReject && onReject(value);
status = 'pending';
//如果狀態還是 peding,表示 resolve 或 reject 還沒執行,那就先把 then 的兩個函式存起來,等 resolve 或 reject 的時候再呼叫
} else {
onResolvedCallback = onFulfill;
onRejectedCallback = onReject;
}
}
//將 then 得到的函式傳給 toDoThen
this.then = function(onFulfill, onReject) {
toDoThen(onFulfill, onReject);
};
//執行 fn
fn(resolve, reject);
return this;
}
複製程式碼
測試一波
var promise = new Promise(function(resolve, reject) {
setTimeout(() => {
resolve('hello');
}, 3000);
});
promise.then(
val => {
console.log('第一次成功', val);
},
val => {
console.log('第一次失敗', val);
}
);
複製程式碼
執行時會列印出 '第一次成功' 和 'hello'
鏈式 then
如果想要實現以下的程式碼,讓第二個 then 的執行函式是根據第一個 then 返回值的布林值來確定的。
promise
.then(
val => {
console.log('第一次成功', val);
return 'world';
},
val => {
console.log('第一次失敗', val);
return false;
}
)
.then(
val => {
console.log('第二次成功', val);
},
val => {
console.log('第二次失敗', val);
}
);
複製程式碼
這裡如果要實現鏈式 then,那麼 then 返回的也必須是一個 Promise 物件,並根據第一個 then 的返回值來確定下一個 then 呼叫哪個函式。
function Promise(fn) {
//初始 status 為 peding
let status = 'pending';
//儲存 then 回撥值
let value;
//儲存 then 的兩個引數
let onResolvedCallback;
let onRejectedCallback;
//設定 fn 的 resolve 函式
function resolve(data) {
//將 status 變成 fulfilled
status = 'fulfilled';
//儲存回撥函式的值
value = data;
toDoThen(onResolvedCallback, onRejectedCallback);
}
//設定 fn 的 reject 函式
function reject(data) {
//將 status 變成 rejected
status = 'rejected';
//儲存回撥函式的值
value = data;
toDoThen(onResolvedCallback, onRejectedCallback);
}
//設定 resolve 或 reject 時的執行體
function toDoThen(onFulfill, onReject) {
//如果是 fulfilled 狀態,表示要執行 then 的第一個函式
if (status === 'fulfilled') {
onFulfill && onFulfill(value);
status = 'pending';
//如果是 rejected 狀態,表示要執行 then 的第二個函式
} else if (status === 'rejected') {
onReject && onReject(value);
status = 'pending';
//如果狀態還是 peding,表示 resolve 或 reject 還沒執行,那就先把 then 的兩個函式存起來,等 resolve 或 reject 的時候再呼叫
} else {
onResolvedCallback = onFulfill;
onRejectedCallback = onReject;
}
}
this.then = function(onFulfill, onReject) {
return new Promise((resolve, reject) => {
toDoThen(
val => {
let result = onFulfill(val);
//根據第一個 then 的返回值來確定下一個 then 的呼叫
result ? resolve(result) : reject(result);
},
err => {
let result = onReject(err);
//根據第一個 then 的返回值來確定下一個 then 的呼叫
result ? resolve(result) : reject(result);
}
);
});
};
//執行 fn
fn(resolve, reject);
return this;
}
複製程式碼
來執行一下
var promise = new Promise(function(resolve, reject) {
setTimeout(() => {
resolve('hello');
}, 3000);
});
promise
.then(
val => {
console.log('第一次成功', val);
return 'world';
},
val => {
console.log('第一次失敗', val);
return false;
}
)
.then(
val => {
console.log('第二次成功', val);
},
val => {
console.log('第二次失敗', val);
}
)
複製程式碼
程式會依次輸出 '第一次成功' 'hello' 和 '第二次成功' 'world' 。
到此就實現了一個簡易版的 Promise,當然,還有很多地方細節沒有考慮到,實現地非常粗糙。