JavaScript是一種屬於網路的指令碼語言,已經被廣泛用於Web應用開發,為使用者提供更流暢美觀的瀏覽效果。
眾所周知,js的事件處理模型決定了它內部很多行為都是非同步的,最常見的如setTimeout、setInterval、我們通常的ajax,當然還有我們的事件,程式碼如:
dom.addEventListener('keydown', function(e){
console.log(e);
})
複製程式碼
這就是一段普通的鍵盤捕獲程式,這本身當然是沒什麼問題的。有問題的是隨著業務越來越複雜,我們需要不斷的藉助非同步的方式處理各種各樣的邏輯,然後程式碼就變成了這樣:
ajax('requestA', function(resA){
//do sth
ajax('requestB', function(resB){
//do sth
ajax('requestC', function(resC){
//do sth
ajax('requestD', function(resD){
//do sth
ajax('requestE', function(resE){
//do sth
ajax('requestF', function(resF){
//do sth
ajax('requestG', function(resG){
//do sth
ajax('requestH', function(resH){
//do sth
})
})
})
})
})
})
})
})
複製程式碼
Promise的出現就是為了解決這種回撥地獄。今天我們就來了解一下Promise~~
一 、初識Promise
什麼是promise?
Promise可能大家都不陌生,因為Promise規範已經出來好一段時間了,同時Promise也已經納入了ES6,而且高版本的chrome、firefox瀏覽器都已經原生實現了Promise,只不過和現如今流行的類Promise類庫相比少些API。
所謂Promise,字面上可以理解為“承諾”,就是說A呼叫B,B返回一個“承諾”給A,然後A就可以在寫計劃的時候這麼寫:當B返回結果給我的時候,A執行方案S1,反之如果B因為什麼原因沒有給到A想要的結果,那麼A執行應急方案S2,這樣一來,所有的潛在風險都在A的可控範圍之內了。
我們從最基礎的用法開始一步一步模仿promise的實現。 Promise是通過new來呼叫,而且需要傳入回撥函式:
var promise = new Promise(function(resolve, reject){
//do something
});
複製程式碼
所以Promise是必須是一個可以建構函式:
複製程式碼
function Promise(executor){
executor();
}
複製程式碼
Promise規範如下: 一個promise可能有三種狀態:等待(pending)、已完成(fulfilled)、已拒絕(rejected)
function Promise(executor){
var self = this;
self.state = 'padding';//初始狀態為pending
executor();
}
複製程式碼
我們會在new的回撥函式裡改變promise的狀態
複製程式碼
var promise = new Promise(function(resolve, reject){
resolve();//把promise的狀態改為已完成
});
複製程式碼
我們的原始碼為
function Promise(executor){
var self = this;
self.state = 'padding';//初始狀態為pending
function resolve(){
self.state = 'resolved';
}
function reject(){
self.state = 'rejected';
}
executor(resolve, reject);
}
複製程式碼
promise的狀態只可能從“等待”轉到“完成”態或者“拒絕”態,不能逆向轉換,同時“完成”態和“拒絕”態不能相互轉換
var promise = new Promise(function(resolve, reject){
resolve();//把promise的狀態改為已完成
reject();//把promise的狀態改為已拒絕
//這樣會2此修改promise的狀態
});
複製程式碼
為了防止這種情況,我們修改我們的程式碼
function Promise(executor){
var self = this;
self.state = 'padding';//初始狀態為pending
function resolve(){
if(self.state == 'padding'){
self.state = 'resolved';
}
}
function reject(){
if(self.state == 'padding'){
self.state = 'rejected';
}
}
executor(resolve, reject);
}
複製程式碼
promise必須實現then方法,then方法接受兩個引數,第一個引數是成功時的回撥,在promise由“等待”態轉換到“完成”態時呼叫,另一個是失敗時的回撥,在promise由“等待”態轉換到“拒絕”態時呼叫。
var promise = new Promise(function(resolve, reject){
resolve();//把promise的狀態改為已完成
});
promise.then(function(){
console.log('promise的狀態改為已完成');//控制檯會列印此句
}, function(){
console.log('promise的狀態改為已拒絕');
});
複製程式碼
var promise = new Promise(function(resolve, reject){
reject();//把promise的狀態改為已拒絕
});
promise.then(function(){
console.log('promise的狀態改為已完成');
}, function(){
console.log('promise的狀態改為已拒絕');//控制檯會列印此句
});
複製程式碼
我們的原始碼為
function Promise(executor){
var self = this;
self.state = 'padding';//初始狀態為pending
function resolve(){
if(self.state == 'padding'){
self.state = 'resolved';
}
}
function reject(){
if(self.state == 'padding'){
self.state = 'rejected';
}
}
executor(resolve, reject);
}
Promise.prototype.then = function(onFulfilled, onRejected){
var self = this;
if(self.state = 'resolved'){
onFulfilled();
}
if(self.state = 'rejected'){
onRejected();
}
}
複製程式碼
很多時候promise是非同步的,比如
let p = new Promise(function(resolve, reject){
setTimeout(function(){
resolve();
}, 3000)
});
複製程式碼
為了解決非同步問題,我們的原始碼改為
function Promise(executor){
let self = this;
self.state = 'padding';
self.onResolvedCallbacks = [];
self.onRejectedCallbacks = [];
function resolve(value){
if(self.state == 'padding'){
self.state = 'resolved'
self.onResolvedCallbacks.forEach(fn=>fn());
}
}
function reject(reson){
if(self.state == 'padding'){
self.state = 'rejected'
self.onRejectedCallbacks.forEach(fn=>fn());
}
}
try{
executor(resolve, reject);
}catch(e){
reject(e);
}
}
Promise.prototype.then = function(onFulfilled, onRejected){
let self = this;
if(self.state === 'resolved'){
onFulfilled();
}
if(self.state === 'rejected'){
onRejected();
}
if(self.state === 'padding'){
self.onResolvedCallbacks.push(function(){
onFulfilled();
});
self.onRejectedCallbacks.push(function(){
onRejected();
});
}
}
複製程式碼
promise規定執行then回撥時,能夠接收一個值,成功有成功的值,失敗有失敗的原因。
let p = new Promise(function(resolve, reject){
setTimeout(function(){
resolve('hello');
}, 3000)
});
p.then(function(data){
console.log(data);//列印出 hello
}, function(err){
console.log(err);
});
複製程式碼
所以我們的原始碼改為
function Promise(executor){
let self = this;
self.state = 'padding';
self.value = undefined;
self.reson = undefined;
self.onResolvedCallbacks = [];
self.onRejectedCallbacks = [];
function resolve(value){
if(self.state == 'padding'){
self.state = 'resolved'
self.value = value
self.onResolvedCallbacks.forEach(fn=>fn());
}
}
function reject(reson){
if(self.state == 'padding'){
self.state = 'rejected'
self.reson = reson
self.onRejectedCallbacks.forEach(fn=>fn());
}
}
try{
executor(resolve, reject);
}catch(e){
reject(e);
}
}
Promise.prototype.then = function(onFulfilled, onRejected){
let self = this;
if(self.state === 'resolved'){
onFulfilled(self.value);
}
if(self.state === 'rejected'){
onRejected(self.reson);
}
if(self.state === 'padding'){
self.onResolvedCallbacks.push(function(){
onFulfilled(self.value);
});
self.onRejectedCallbacks.push(function(){
onRejected(self.reson);
});
}
}
複製程式碼
下一片文章我們接著來解決鏈式呼叫的問題