Javascript語言的執行環境是"單線程"(single thread)。所謂"單線程",就是指一次只能完成一件任務。若是有多個任務,就必須排隊,前面一個任務完成,再執行後面一個任務,以此類推。ajax
這種模式的好處是實現起來比較簡單,執行環境相對單純;壞處是隻要有一個任務耗時很長,後面的任務都必須排隊等着,會拖延整個程序的執行。常見的瀏覽器無響應(假死),每每就是由於某一段Javascript代碼長時間運行(好比死循環),致使整個頁面卡在這個地方,其餘任務沒法執行。編程
爲了解決這個問題,Javascript語言將任務的執行模式分紅兩種:同步(Synchronous)和異步(Asynchronous)。json
異步模式"編程的4種方法:回調函數、事件監聽、發佈/訂閱、Promises對象。
還有generator、async/await.數組
本文嘗試說一下對Promise的理解及如何實現。
1.Promise原理promise
promise對象有三種狀態,pending、fulfilled和rejected。promise對象內部保存一個須要執行一段時間的異步操做,當異步操做執行結束後能夠調用resolve或reject方法,來改變promise對象的狀態,狀態一旦改變就不能再變。new一個promise後能夠經過then方法,指定resolved和rejected時的回調函數。下面是咱們平常使用Promise的代碼邏輯。
let p = new Promise((resolve,reject)=>{瀏覽器
$.ajax({ url: "../www/data.txt", dataType: "json", success(data){ resolve(data); }, error(err){ reject(err); } }); }); p.then(function(data){ alert("success"+data); },function(err){ alert("failed"); })
結合Promise A+規範,咱們就能夠分析一下咱們要實現一個什麼東西:異步
實現一個狀態機,有三個狀態,pending、fulfilled、rejected,狀態之間的轉化只能是pending->fulfilled、pending->rejected,狀態變化不可逆。
實現一個then方法,能夠用來設置成功和失敗的回調
then方法要能被調用屢次,因此then方法須要每次返回一個新的promise對象,這樣才能支持鏈式調用。
構造函數內部要有一個value值,用來保存上次執行的結果值,若是報錯,則保存的是異常信息。
2.實現原理async
2.1實現狀態機函數
那咱們如今就按照上面提到的原理和規範來實現這個Promise構造函數。
class myPromise {this
constructor(executor) { this.status = PENDING; this.value = ''; this.Resolve = this.resolve.bind(this); this.Reject = this.reject.bind(this); this.then= this.then.bind(this); executor(this.Resolve, this.Reject); } resolve(value) { if (this.status === PENDING) { this.value = value; this.status = FULFILLED; } } reject(value) { if (this.status === PENDING) { this.value = value; this.status = REJECTED; } } then(onfulfilled, onrejected) { if (this.status === FULFILLED) { onfulfilled(this.value); } if (this.status === REJECTED) { onrejected(this.value); } } } const PENDING = 'pending'; const FULFILLED = 'fulfilled'; const REJECTED = 'rejected'; const test = new myPromise((resolve, reject) => { resolve(100); }); test.then((data) => { console.log(data); }, (data) => { });
由於Promise是一個構造函數,使用ES6的寫法,首先想到的就是有顯式constructor聲明的class。上面就是咱們用class的實現,能夠看到這樣咱們就實現了這個狀態機,有status, value兩個屬性和resolve, reject, then三個函數;同時它有pending, fulfilled和rejected三個狀態,其中pending就能夠切換爲fulfilled或者rejected兩種。
運行一下,輸出了100,可是如今其實不是一個異步處理方案,代碼先運行了resolve(100)而後又運行了then函數,其實對於異步的狀況沒有處理,不信的話就給resolve加一個setTimeout,好了,代碼又沒有輸出了。
2.2 實現異步設置狀態
做爲一個異步處理的函數,在使用的時候,咱們確定是會先設置好不一樣異步返回後的處理邏輯(即then的成功、失敗調用函數),而後安心等待異步執行,最後再異步結束後,系統會自動根據咱們的邏輯選擇調用不一樣回調函數。換句話說,then函數要對status爲pending的狀態進行處理。處理的原理是設置兩個數組,在pending狀態下分別保存成功和失敗回調函數,當狀態改變後,再根據狀態去調用數組中保存的回調函數。
class myPromise {
constructor (executor) {
this.status = PENDING; this.value = ''; this.onfulfilledArr = []; this.onrejectedArr = []; this.resolve = this.resolve.bind(this); this.reject = this.reject.bind(this); this.then = this.then.bind(this); executor(this.resolve, this.reject);
}
resolve (value) {
if (this.status === PENDING) { this.value = value; this.onfulfilledArr.forEach(item => { item(this.value); }) this.status = FULFILLED; }
}
reject (value) {
if (this.status === PENDING) { this.value = value; this.onrejectedArr.forEach(item => { item(this.value); }) this.status = REJECTED; }
}
then (onfulfilled, onrejected) {
if (this.status === FULFILLED) { onfulfilled(this.value); } if (this.status === REJECTED) { onrejected(this.value); } if (this.status === PENDING) { this.onfulfilledArr.push(onfulfilled); this.onrejectedArr.push(onrejected); }
}
}
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
const test = new myPromise((resolve, reject) => {
setTimeout(() => {
resolve(100);
}, 2000)
});
test.then((data) => {
console.log(data);
},(data) => {});
能夠這樣理解
new myPromise 有異步代碼
setTimeout(() => {
resolve(100);
}, 2000)
js是單線程的,這個時候會先把這個任務添加到定時觸發器線程中去(計時完畢後添加到事件隊列中,等待js引擎空閒後執行),先去執行下面的同步代碼
test.then((data) => {
console.log(data);
},(data) => {});
完成輸出及狀態改變。
可是Promise的一大特色就是能夠鏈式調用,即test.then(success, fail).then(success, fail)...這就須要then返回一個新的Promise對象,而咱們的程序如今明顯的是不支持的。那麼繼續改一下。
2.3 實現鏈式調用
再觀察一下鏈式調用,若是成功和失敗的函數中有返回值,這個值要做爲參數傳給下個then函數的成功或失敗回調。因此咱們要在返回的new Promise中調用相應的函數。
class myPromise {
constructor (executor) {
this.status = PENDING; this.value = ''; this.onfulfilledArr = []; this.onrejectedArr = []; this.resolve = this.resolve.bind(this); this.reject = this.reject.bind(this); this.then = this.then.bind(this); executor(this.resolve, this.reject);
}
resolve (value) {
if (this.status === PENDING) { this.value = value; this.onfulfilledArr.forEach(item => { item(this.value); }) this.status = FULFILLED; }
}
reject (value) {
if (this.status === PENDING) { this.value = value; this.onrejectedArr.forEach(item => { item(this.value); }) this.status = REJECTED; }
}
then (onfulfilled, onrejected) {
if (this.status === FULFILLED) { const res = onfulfilled(this.value); return new Promise(function(resolve, reject) { resolve(res); }) } if (this.status === REJECTED) { const res = onrejected(this.value); return new Promise(function(resolve, reject) { reject(res); }) } if (this.status === PENDING) { const self = this; return new Promise(function(resolve, reject) { self.onfulfilledArr.push(() => { const res = onfulfilled(self.value) resolve(res); }); self.onrejectedArr.push(() => { const res = onrejected(self.value) reject(res); }); }) }
}
}
const PENDING = 'pending';
const FULFILLED = 'fulfilled';
const REJECTED = 'rejected';
const test = new myPromise((resolve, reject) => {
setTimeout(() => {
resolve(100);
}, 2000)
});
test.then((data) => {
console.log(data);
return data + 5;
},(data) => {})
.then((data) => {
console.log(data)
},(data) => {});
再運行一下,輸出100,105。好了,一個簡單的Promise就實現好了。