• 前端通信: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
        };
        //存储当前变量的回调函数和标记对象为promise
        promise._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
                    };
                    //存储当前变量的回调函数和标记对象为promise
                    promise._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等等一层接一层。还是那句老话,革命尚未成功,同志仍需努力,我和你们同在。

    马上又要回去重新找工作了,希望可以找到如意的工作,毕竟为了错开金三银四,希望一切都会好起来。一起加油吧。

  • 相关阅读:
    【题解】Atcoder ARC#76 F-Exhausted?
    【题解】SCOI2006萌萌哒
    【题解】51nod1967 路径定向
    巧克力之树 [点分治+树状数组]
    thr [树链剖分+dp]
    [bzoj4361] isn [树状数组+dp+容斥原理]
    [POI2015][bzoj4383] Pustynia [线段树优化建图+拓扑排序]
    [HNOI2015][bzoj4011] 落叶枫音 [拓扑DP]
    [Atcoder Grand Contest 006 F][AGC006F] Blackout [染色]
    [bzoj4712] 洪水 [树链剖分+线段树+dp]
  • 原文地址:https://www.cnblogs.com/GerryOfZhong/p/7096792.html
Copyright © 2020-2023  润新知