摘要:一個后可以通過方法,指定和時的回調函數。構造函數內部要有一個值,用來保存上次執行的結果值,如果報錯,則保存的是異常信息。因為是一個構造函數,使用的寫法,首先想到的就是有顯式聲明的。
Javascript語言的執行環境是"單線程"(single thread)。所謂"單線程",就是指一次只能完成一件任務。如果有多個任務,就必須排隊,前面一個任務完成,再執行后面一個任務,以此類推。
這種模式的好處是實現起來比較簡單,執行環境相對單純;壞處是只要有一個任務耗時很長,后面的任務都必須排隊等著,會拖延整個程序的執行。常見的瀏覽器無響應(假死),往往就是因為某一段Javascript代碼長時間運行(比如死循環),導致整個頁面卡在這個地方,其他任務無法執行。
為了解決這個問題,Javascript語言將任務的執行模式分成兩種:同步(Synchronous)和異步(Asynchronous)。
異步模式"編程的4種方法:回調函數、事件監聽、發布/訂閱、Promises對象。
還有generator、async/await.
本文嘗試說一下對Promise的理解及如何實現。
1.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.實現原理
2.1實現狀態機
那我們現在就按照上面提到的原理和規范來實現這個Promise構造函數。
class myPromise {
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就實現好了。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/52860.html
摘要:一個后可以通過方法,指定和時的回調函數。構造函數內部要有一個值,用來保存上次執行的結果值,如果報錯,則保存的是異常信息。因為是一個構造函數,使用的寫法,首先想到的就是有顯式聲明的。 showImg(https://segmentfault.com/img/bVbffEu?w=530&h=253); Javascript語言的執行環境是單線程(single thread)。所謂單線程,就...
摘要:意味著操作成功完成。狀態的對象可能觸發狀態并傳遞一個值給相應的狀態處理方法,也可能觸發失敗狀態并傳遞失敗信息。測試用例測試用例方法返回一個帶有拒絕原因參數的對象。 Promise基本用法 Promise 對象是一個代理對象,被代理的值在Promise對象創建時可能是未知的。 它允許你為異步操作的成功和失敗分別綁定相應的處理方法(handlers)。 這讓異步方法可以像同步方法那樣返回值...
摘要:它之后能夠被使用到很多場景中其他處理請求和響應的方式,甚至任何需要生成自己的響應的方式。總結到這里都講完了,其實沒什么難度,主要是自己項目中遇到了,但是中沒有這個方法啊。所以就想著實現了一個,因為其他的方法也都封裝,不差這一個了。 Fetch 提供了對?Request?和?Response?(以及其他與網絡請求有關的)對象通用的定義。它之后能夠被使用到很多場景中:service wor...
摘要:等待的基本語法該關鍵字的的意思就是讓編譯器等待并返回結果。這里并不會占用資源,因為引擎可以同時執行其他任務其他腳本或處理事件。接下來,我們寫一個火箭發射場景的小例子不是真的發射火箭 本文由云+社區發表 本篇文章,小編將和大家一起學習異步編程的未來——async/await,它會打破你對上篇文章Promise的認知,竟然異步代碼還能這么寫! 但是別太得意,你需要深入理解Promise后,...
閱讀 466·2021-10-09 09:57
閱讀 470·2019-08-29 18:39
閱讀 811·2019-08-29 12:27
閱讀 3028·2019-08-26 11:38
閱讀 2667·2019-08-26 11:37
閱讀 1285·2019-08-26 10:59
閱讀 1376·2019-08-26 10:58
閱讀 989·2019-08-26 10:48