前端通信:ajax设计方案(五)--- 集成promise规范,更优雅的书写代码(改迭代已作废,移步迭代10)...
?該迭代已作廢,最新的請移步這里:https://www.cnblogs.com/GerryOfZhong/p/10726306.html
? ? ? 距離上一篇博客書寫,又過去了大概幾個月了,這段時間暫時離開了這個行業,讓大腦休息一下。一個人旅行,一個人休息,正好也去完成一個目標 --- 擁有自己的駕照。當然,也把自己曬的黑漆馬虎的。不過這一段時間雖然在技術上沒有學太多東西,但是在心態上給了自己一個沉淀的機會,感覺自己變得更加沉穩和成熟,感覺這就是自己需要找到的自己,回歸自我。好了,廢話不多說了,雖然技術上沒有學一些新的東西,但是欠的東西還是要補回來的。正如這篇博客,前端Promise規范的實現與ajax技術的集成,當時github上一個用戶提的,既然寫了ajax,那么Promise的規范,更優雅的操作異步也應該有的,當時記下了,現在補回來。回歸正題,下面介紹一些概念。
- Promise? ES6中最重要的特性之一,就是代表了未來某個將要發生的事件(通常是一個異步操作)。它的好處在于,有了Promise對象,就可以將異步操作以同步操作的流程表達出來,避免了層層嵌套的回調函數。
- js對象的繼承和傳遞 在js中是沒有所謂的繼承概念的,繼承通常是指后臺面向對象編程中對象之間的復用。因為js被叫做腳本語言,所以沒有這個概念,但是在js中都可以模擬實現的(apply,call,prototype)。其實說通俗點所謂的繼承就是作用域的傳遞。
- ajax和promise的緣分 因為ajax是一個異步的請求(雖然也可以使用同步),而promise這個狀態機也正好可以處理異步操作。兩者的相結合的產物,將是一個優雅而又快捷的產物,這個將在后面的介紹中展現。
?
工具準備:
1. 前端代碼,自己實現的promise規范代碼,以及集成現有es6規范的代碼。
2. nginx服務器,做分離,反向代理后臺代碼
3. IIS服務器,部署后臺請求(模擬一般請求和高延遲請求)
4. 各大兼容和不兼容promise的瀏覽器(做測試)
?
前端Promise代碼實現:
/*** Created by gerry.zhong on 2017/6/21.*/ var Promise = function(fn){var promise = this;//狀態機的狀態var PROMISESTATE = {PENDING : 0 ,FULFILLED : 1 ,REJECTED : 2};//存儲當前變量的回調函數和標記對象為promisepromise._fullCalll =[],promise._rejCall = [];promise._name = "promise";//執行過程中的狀態變化(初始化狀態為默認狀態)var _state = PROMISESTATE.PENDING;//回調函數的參數var _value = undefined;//狀態變更function setState(stateT,valueT){var promise = this;_state = stateT;_value = valueT;handleFun.call(promise); //傳遞作用域,并且執行回調函數 };//根據狀態處理回調function handleFun(){var promise = this,isThen;if (_state === PROMISESTATE.FULFILLED &&typeof promise._fullCalll[0] === 'function') {isThen = promise._fullCalll[0](_value);};if (_state === PROMISESTATE.REJECTED &&typeof promise._rejCall[0] === 'function') {isThen = promise._rejCall[0](_value);};//對于是否可以繼續進行then做判斷// 1. 不可then的,直接return結束(條件:無返回值、返回值不是promise對象的)// 2. 對于可以then的,將then的回調進行處理,然后對象之間傳遞。if (isThen === undefined || !(typeof isThen === 'object' && isThen._name === 'promise')) return;promise._fullCalll.shift(); promise._rejCall.shift(); //清除當前對象使用過的對調isThen._fullCalll =promise._fullCalll;isThen._rejCall = promise._rejCall; //將剩下的回調傳遞到下一個對象 };//promimse入口function doResolve(fn){var promise = this;fn(function(param) {setState.call(promise,PROMISESTATE.FULFILLED,param);}, function(reason) {setState.call(promise,PROMISESTATE.REJECTED,reason);});};//函數then,處理回調,返回對象保證鏈式調用this.then = function(onFulfilled,onRejected) {this._fullCalll.push(onFulfilled);this._rejCall.push(onRejected);return this;}doResolve.call(promise,fn); }具體思路如下:
?
解決瀏覽器的差異性和兼容性代碼
if (!window.Promise) tool.createPromise(); //保證瀏覽器的兼容性?
tool.createPromise代碼
//如果瀏覽器不支持Promise特性,將用簡易的promise代替(IE11-都不支持ES6 Promise)createPromise:function(){var newPromise = function(fn){var promise = this;//狀態機的狀態var PROMISESTATE = {PENDING : 0 ,FULFILLED : 1 ,REJECTED : 2};//存儲當前變量的回調函數和標記對象為promisepromise._fullCalll =[],promise._rejCall = [];promise._name = "promise";//執行過程中的狀態變化(初始化狀態為默認狀態)var _state = PROMISESTATE.PENDING;//回調函數的參數var _value = undefined;//狀態變更function setState(stateT,valueT){var promise = this;_state = stateT;_value = valueT;handleFun.call(promise); //傳遞作用域,并且執行回調函數 };//根據狀態處理回調function handleFun(){var promise = this,isThen;if (_state === PROMISESTATE.FULFILLED &&typeof promise._fullCalll[0] === 'function') {isThen = promise._fullCalll[0](_value);};if (_state === PROMISESTATE.REJECTED &&typeof promise._rejCall[0] === 'function') {isThen = promise._rejCall[0](_value);};//對于是否可以繼續進行then做判斷// 1. 不可then的,直接return結束(條件:無返回值、返回值不是promise對象的)// 2. 對于可以then的,將then的回調進行處理,然后對象之間傳遞。if (isThen === undefined || !(typeof isThen === 'object' && isThen._name === 'promise')) return;promise._fullCalll.shift(); promise._rejCall.shift(); //清除當前對象使用過的對調isThen._fullCalll =promise._fullCalll;isThen._rejCall = promise._rejCall; //將剩下的回調傳遞到下一個對象 };//promimse入口function doResolve(fn){var promise = this;fn(function(param) {setState.call(promise,PROMISESTATE.FULFILLED,param);}, function(reason) {setState.call(promise,PROMISESTATE.REJECTED,reason);});};//函數then,處理回調,返回對象保證鏈式調用this.then = function(onFulfilled,onRejected) {this._fullCalll.push(onFulfilled);this._rejCall.push(onRejected);return this;}doResolve.call(promise,fn);};window.Promise = newPromise;},這樣就保證了,不管在兼容和不兼容Promise的瀏覽器中,都可以使用Promise,優雅的來操作異步了。
?
ajax集成proise代碼(默認只開放了post和get方法,其他可自己拓展)
//集成promise的ajax請求(默認設置post和get請求,如有其他需求,可自己拓展)promiseAjax:function (url,data,type){if (!window.Promise) tool.createPromise(); //保證瀏覽器的兼容性return new Promise(function(resolve, reject){if (type === undefined) ajax.post(url,data,resolve,reject);else ajax.get(url,data,resolve,reject);});},?
測試環節
對于網上很多人寫的Promise代碼仔細觀摩和研究,發現很多問題。
a. 對于并發Promise,會出現異步錯亂,發起者和接受者錯亂
b. 對于多then的情況,異步響應的不確定(高低延遲),錯亂。
c. Promise代碼實現的復雜性,多繁瑣,難理解,思路不明確。
? 針對以上問題,進行重要測試。
?
測試前端代碼
ajax.promiseAjax("api/ajax/postReq/",{"name":"q","age": 2}).then(function(value){console.log("一般請求q"+value);return ajax.promiseAjax("api/ajax/postReqSleep/",{"name":"w","age": 2});}).then(function(value){console.log("高延遲請求w:"+value);return ajax.promiseAjax("api/ajax/postReq/",{"name":"r","age": 2});}).then(function(value){console.log("一般請求r:"+value);});ajax.promiseAjax("api/ajax/postReqSleep/",{"name":"q1","age": 2}).then(function(value){console.log("高延遲請求q1"+value);return ajax.promiseAjax("api/ajax/postReqSleep/",{"name":"w2","age": 2});}).then(function(value){console.log("高延遲請求w2:"+value);return ajax.promiseAjax("api/ajax/postReq/",{"name":"r3","age": 2});}).then(function(value){console.log("一般請求r3:"+value);});后端模擬延遲請求代碼(C#)
[Route("postReqSleep")]public string postRequestTSleep([FromBody]Param param){Thread.Sleep(5000); //掛起5s 做延遲String result = "post請求成功:" + param.name + "-" + param.age;return result;}?
?
測試結果:
chrome
ie8-11
edge
firefox
360瀏覽器
safair
?
??代碼已集成github:https://github.com/GerryIsWarrior/ajax?? ??點顆星星是我最大的鼓勵,有什么問題可以博客、郵箱、github上留言
?還有最重要的一點,如果有問題歡迎指出來,我在github上維護這個庫,這段時間專注于前端的通信技術的研究,ajax基本完成,馬上進入SSE推送技術研究狀態
?
研究Promise這個內容,研究和參考了很多別人的代碼,從高別人的代碼中看到了各種問題,然后在自己代碼中測試發現和改正。所以沒有什么是絕對正確的,我寫的可能也有問題,希望大家在研究和發展的基礎上一起改進。畢竟對于前端來說,技術更新太快,ES5 ES6等等一層接一層。還是那句老話,革命尚未成功,同志仍需努力,我和你們同在。
?
馬上又要回去重新找工作了,希望可以找到如意的工作,畢竟為了錯開金三銀四,希望一切都會好起來。一起加油吧。
轉載于:https://www.cnblogs.com/GerryOfZhong/p/7096792.html
總結
以上是生活随笔為你收集整理的前端通信:ajax设计方案(五)--- 集成promise规范,更优雅的书写代码(改迭代已作废,移步迭代10)...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 2017.4.14 java基本类型和包
- 下一篇: An Overview of Cisco