Promise是爲了解決Javascript回調嵌套過多致使回調地獄(callbackhell)而產生的。目前已被歸入了es2015規範,主流瀏覽器都支持Promise。爲了在工做中更好的運用Promise,咱們須要理解Promise規範與內部實現機制,下面咱們來手動實現一個Promise。javascript
在寫代碼以前讓咱們先了解下 Promise/A+規範。
一個promise有三種狀態:java
借用這張來自MDN的流程圖咱們能夠清晰的看到 Promise 狀態的流轉過程。promise
下面咱們來實現一個簡單版的 Promise:瀏覽器
function Promise1(executor){
let self = this;
self.status = 'pending';
self.value = undefined;
self.reason = undefined;
function resolve(value) {
if(self.status==='pending'){
self.status = 'fullfilled';
self.value = value;
}
}
function reject(reason) {
if(self.status==='pending') {
self.status = 'rejected';
self.reason = reason;
}
}
try{
executor(resolve,reject);
}catch(e) {
reject(e);
}
}
Promise1.prototype.then = function(onFullfilled,onRejected) {
if(this.status==='fullfilled') {
onFullfilled(this.value);
}
if(this.status==='rejected') {
onRejected(this.reason);
}
}
//測試
let p= new Promise1(function(resolve,reject){resolve(1)});
p.then(function(x){console.log(x)})
//輸出1
複製代碼
如今,咱們實現了最簡單的 Promise。以上版本的Promise是存在不少問題的。爲何呢?最大的問題是它不支持異步,然而在現實中,Promise絕大多數使用場景都是異步。讓咱們來爲 Promise 加入異步功能。異步
const PENDING = 'pending';
const FULFILLED = 'fullfilled';
const REJECTED = 'rejected';
function Promise1(executor){
let self = this;
self.status = PENDING;
self.value = undefined;
self.reason = undefined;
self.fullfilledCallbacks = [];
self.rejectedCallbacks = [];
function resolve(value) {
if(value instanceof Promise) {
value.then(resolve,reject);
}
setTimeout(function(){
if(self.status===PENDING){
self.status = FULFILLED;
self.value = value;
self.fullfilledCallbacks.forEach(function(cb){
cb(self.value)
})
}
})
}
function reject(reason) {
setTimeout(function(){
if(self.status===PENDING) {
self.status = REJECTED;
self.reason = reason;
self.rejectedCallbacks.forEach(function(cb){
cb(self.reason);
})
}
})
}
try{
executor(resolve,reject);
}catch(e) {
reject(e);
}
}
Promise1.prototype.then = function(onFulfilled,onRejected) {
let self = this;
return new Promise1(function(resolve,reject){
function success(value) {
let _value = (typeof onFulfilled === 'function' && onFulfilled(value)) || value;
resolve(_value)
}
function error(reason) {
let _reason = (typeof onRejected === 'function' && onRejected(reason)) || reason;
reject(_reason);
}
if(self.status==PENDING) {
self.fullfilledCallbacks.push(success);
self.rejectedCallbacks.push(error);
} else if(self.status==FULLFILLED){
success.call(this,this.value)
} else if(self.status==REJECTED) {
error.call(this,this.reason);
}
})
}
複製代碼
以上代碼中,咱們作了以下更改:函數
下面讓咱們來爲Promise 添加錯誤處理以及靜態方法:學習
//錯誤處理
Promise1.prototype.catch = function(onRejected) {
return this.then(null, onRejected);
}
//返回fullfilled Promise對象
Promise1.resolve = function (value) {
return new Promise1(resolve => {
resolve(value);
});
}
//返回 rejected Promise 對象
Promise1.reject = function (reason) {
return new Promise1((resolve, reject) => {
reject(reason);
});
}
//Promise.all方法
Promise1.all = function(promises) {
function gen(length, resolve) {
let count = 0;
let values = [];
return function(i, value) {
values[i] = value;
if (++count === length) {
resolve(values);
}
}
}
return new Promise1((resolve, reject) => {
let done = gen(promises.length, resolve);
promises.forEach((promise, index) => {
promise.then((value) => {
done(index, value)
}, reject)
})
})
}
//Promise.race方法
Promise1.race = function(promises) {
return new Promise1((resolve, reject) => {
promises.forEach((promise, index) => {
promise.then(resolve, reject);
});
});
}
複製代碼
這裏有個問題,就是在當咱們console.log(Promise1.resolve('a'))的時候,我發現打印出來的狀態居然是 pending狀態,我猜測緣由是應該是resolve中函數異步執行,在當咱們console的時候setTimeout中代碼未執行,因此我給出的解決方法是將狀態變化與賦值移到setTimeout外面,這樣就不會產生剛纔的問題了,更改後代碼長這樣:測試
function resolve(value) {
if(value instanceof Promise) {
value.then(resolve,reject);
}
self.status = FULFILLED;
self.value = value;
setTimeout(function(){
if(self.status===PENDING){
self.fullfilledCallbacks.forEach(function(cb){
cb(self.value)
})
}
})
}
function reject(reason) {
self.status = REJECTED;
self.reason = reason;
setTimeout(function(){
if(self.status===PENDING) {
self.rejectedCallbacks.forEach(function(cb){
cb(self.reason);
})
}
})
}
複製代碼
通過以上實踐,咱們成功的手寫了一個功能完備的 Promise。這裏給個人最大啓發是若是咱們想學習一個東西,必須深刻到它的底層,瞭解它的運行原理與具體實現方法,而且能夠造一個簡單的輪子,這樣纔算咱們掌握了該知識點。從前的我對於這一點沒有關注的太多,致使在用某個知識點時只是掌握的它的表層用法,在高級一點的使用場景時徹底不會運用。之後我會更加註重源碼方面的學習,彌補我這方面的不足。ui