当前位置: 首页 > news >正文

Javascript Promise 学习(上)

Promise 就是处理异步的一个规范方法

a();
b();
alert("a");
如果a() 里面有一个ajax 或者settimeout 
那么alert("a") 会先跑
这就是异步了。
从前我们用一堆callBack函数来解决问题,但是这样写不好看。
promise 的写法美丽多了
依据上面的例子
a().then(b).then(function(){
alert("");
})
这样它会先跑完 a -> b - > alert("");

虽然很多类库都实现了这个功能,甚至游览器也有自带的,而且ecma6好像还会有更好更简单的方法来解决异步的操作 ! 
but ! 人生最鸡巴的就是这个 but! 
当作学习,我还是自己写了一个版本。
这个版本不是 promise a 也不是promise a+ 也不是jQuery Defferd 规范
它是给我自己用的!

个人觉得重点思想就是在 then 的时候返回一个 next promise , 而next promise 记入在上一个promise中,这样就创建了一个promise 连环链

而一个回调函数返回的是promise 或者我们叫thenAble函数,那么我们就把原先的链连接去它的后面就可以了。 

     function Promise(fn) {
        //如果有放fn进来,那么就调用它,把 pass & fail 做参数传进去
        //这样fn 内就可以直接 publish了 
        if (G.isFunction(fn)) {
            var promise = new Promise();
            fn.apply(null, [promise.pass.bind(promise), promise.fail.bind(promise)]);
            return promise;
        }
        this.status = "pending";
        this.data;
        this._nextPromise; //保存下一个的指针,这样才可以连环触发
        this._passFn_list = [];
        this._failFn_list = [];
        this._isAllPassRequest = true; //all or any 用的
        //注释
        //要改要潜水
        //这里只说简单的使用说明吧 
        /*
        //方便的初始化调用法
        new Promise(function (pass, fail) {
            setTimeout(pass, 100, "zzz"); //第三para是传给方法的参数 = pass(zzz)
        }).then(fnPass, fnFail);
        //一般调用法
        a().then(b).then(c);
        function a() {
            var promise = new Promise();
            setTimeout(function () {
                promise.pass("data");
            }, 1000);
            return promise;
        }
        //all 和 any 参数是array,它会并发处理 
        //等待全部处理完验证每个的返回
        //unknow 算 true 
        //(note: 一个promise只保存all or any 1condition default是 all)
        //如果要求是 all ,那么回来的全部都必须all 就调用nextPromise.pass else fail
        //如果要求是 any ,那么回来的要有至少一个是pass 就调用nextPromise.pass else fail
        a().all([b, c], [c, b]).then(b, c);
        //.catch, .complete 都是语法糖罢了
    
        //异步loop调用, 这个比较复杂,以后还可以有更多变化
        var list = [a, a, a];
        var promiseTemp;
        for (var i = 0; i < list.length; i++) {
            var promise = (promiseTemp == undefined) ? list[i]() : promiseTemp.then(list[i]);
            promiseTemp = promise;
        }
        */
        //简单说明:
        /*
        两大关键方法
        then : 把参数加入fn_list中, 然后new 新的promise, 当前的promise.next 指向新的promise(循环链就开始了) ,然后把新的promise 返回出去
        pass/fail : 把fn_list中的方法全部拿出来,然后"并发处理", 检查每一个是不是返回promise, 是的话要记入和跟踪,跟踪其实就是为这个promise加一个then,然后把职责链放进去,交替的感觉。
     
        调用then/all/any的时候如果promise的status不是pending会直接add and 发布data
        在并发方法之后 fnList会被清除干净,也就是说一个promise的fnlist的每个方法只会被执行一次
        */
    }
    Promise.prototype._concatFnList = function (passFn_list, failFn_list) {
        //all 和 any 用来concat fn list 的
        if (passFn_list != undefined && Array.isArray(passFn_list)) {
            this._passFn_list = this._passFn_list.concat(passFn_list);
        }
        if (failFn_list != undefined && Array.isArray(failFn_list)) {
            this._failFn_list = this._failFn_list.concat(failFn_list);
        }
    }
    Promise.prototype._getFinalDataByDataRecordList = function (dataRecord_list) {
        //dataRecord_list 是array , 因为大部分时候我们是用一个data罢了,所以这边做一些小过滤
        var finalValue;
        if (dataRecord_list.length == 1) {
            finalValue = dataRecord_list[0].data; //抽取data出来就够了
        }
        else {        
            dataRecord_list.orderBy("index");//排好位置(因为all/any是并发,不会按照顺序回来)
            finalValue = dataRecord_list.map(function (obj) {
                delete obj.index; //洗掉属性 index 排位之后就没用了
                return obj;
            });
        }
        return finalValue;
    }
    Promise.prototype._publish = function (fn_list, that) {
        //pass 和 fail 的共用过程
        if (fn_list.length > 0) {
            var returnPromiseCountRecord = 0; //for all & any 多个并发,同时会有多个promise函数
            var dataRecord_list = []; //收集全部函数回来的data {index,data,status}                              
            for (var i = 0, l = fn_list.length; i < l; i++) {
                var fn = fn_list[i];
                var returnValue = fn.call(that, that.data); //挺关键的一句,这里会确定他返回的是不是promise
                (function (i) {
                    function callBack(data) {
                        //这里是我们外面的promise链 publish触发的 
                        var result = { index: i, status: this.status, data: data };
                        dataRecord_list.push(result) //record data
                        returnPromiseCountRecord--; //之前我们累加计算,这里publish了就要开始累减回去了
                        //一直到0就准备做ending然后继续外面的职责链了
                        if (returnPromiseCountRecord == 0) {
                            var is_allPass = dataRecord_list.every(function (v) {
                                return (v.status == "unknow" || v.status == "pass");
                            });
                            var is_anyPass = dataRecord_list.some(function (v) {
                                return (v.status == "unknow" || v.status == "pass");
                            });
                            var finalValue = that._getFinalDataByDataRecordList(dataRecord_list);
                            //这里会依据 all or any 做处理 觉得调用下一家的 pass or fail
                            if ((that._isAllPassRequest && is_allPass) || (!that._isAllPassRequest && is_anyPass)) {
                                that._nextPromise.pass(finalValue);
                            }
                            else {
                                that._nextPromise.fail(finalValue);
                            }
                        }
                    }
                    if (returnValue instanceof Promise) {
                        //是promise的话就要 +count,然后+then给他,把职责链给他,等待他触发
                        returnPromiseCountRecord++;
                        var otherPromise = returnValue;
                        otherPromise.complete(callBack);
                    }
                    else {
                        //不是的话就push data 就好,status : unknow 也算 pass
                        var result = { index: i, status: "unknow", data: returnValue };
                        dataRecord_list.push(result);
                    }
                })(i)
            }
            fn_list.length = 0; //运行完就clear掉
            //没有return任何thenable方法
            if (returnPromiseCountRecord == 0) {
                var finalValue = that._getFinalDataByDataRecordList(dataRecord_list);
                that._nextPromise.pass(finalValue); //因为没有pass fail 参考所以一定是pass 
            }
        }
        else {
            //如果没有效应就去找小一家promise效应
            if (that._nextPromise) {
                that._nextPromise[that.status](that.data);
            }
        }
    }
    Promise.prototype.all = function (passFn_list, failFn_list) {
        this._concatFnList(passFn_list, failFn_list);
        if (this.status != "pending") this._alreadyPublish();
        return this._nextPromise || (this._nextPromise = new Promise());
    }
    Promise.prototype.any = function (passFn_list, failFn_list) {
        this._concatFnList(passFn_list, failFn_list);
        this._isAllPassRequest = false;
        if (this.status != "pending") this._alreadyPublish();
        return this._nextPromise || (this._nextPromise = new Promise());
    }
    Promise.prototype.then = function (passFn, failFn) {
        if (passFn != undefined) this._passFn_list.push(passFn);
        if (failFn != undefined) this._failFn_list.push(failFn);
        if (this.status != "pending") this._alreadyPublish();
        return this._nextPromise || (this._nextPromise = new Promise());
    }
    Promise.prototype._alreadyPublish = function () {
        var that = this;
        setTimeout(function () {
            that[that.status](that.data);
        }, 0);
    }
    Promise.prototype.pass = function (data) {
        var that = this;
        this.status = "pass";
        this.data = data;
        var passFn_list = this._passFn_list;
        that._publish(passFn_list, that);
    }
    Promise.prototype.fail = function (data) {
        var that = this;
        this.status = "fail";
        this.data = data;
        var failFn_list = this._failFn_list;
        that._publish(failFn_list, that);
    }
    Promise.prototype.complete = function (completeFn) {
        return this.then(completeFn, completeFn);
    }
    Promise.prototype.catch = function (catchFn) {
        return this.then(void 0, catchFn);
    }
    Promise.prototype.getLastPromise = function () {
        var promise = this;
        for (var i = 0; i < Number.MAX_VALUE; i++) {
            if (promise._nextPromise === undefined) {
                return promise;
            }
            else {
                promise = promise._nextPromise;
            }
        }
    }
    G.s.Promise = Promise;

 

转载于:https://www.cnblogs.com/keatkeat/p/3896586.html

相关文章:

  • windows下mongoDB的环境配置
  • 使用Sublime Text 2 编辑Markdown
  • Linux快速配置 VIM 实现语法高亮 补全 缩进等功能
  • 03Go 类型总结
  • PHP打印常量
  • Route配置实现通信实验报告
  • 区块链技术特点之去中心化特性
  • SqlBulkCopy类进行大数据(10000万条以上)插入测试
  • 使用阿里云发布分布式网站,开发时候应该注意什么?
  • 【Linux系统编程】快速查找errno错误码信息
  • 关于aspx.designer.cs的研究
  • 精彩代码 vue.js
  • 霍夫变换直线检测
  • Netflix 混沌工程手册 Part 3:实践方法
  • 又一款博客园Android客户端低调推出
  • “Material Design”设计规范在 ComponentOne For WinForm 的全新尝试!
  • 【JavaScript】通过闭包创建具有私有属性的实例对象
  • electron原来这么简单----打包你的react、VUE桌面应用程序
  • Gradle 5.0 正式版发布
  • Intervention/image 图片处理扩展包的安装和使用
  • JavaSE小实践1:Java爬取斗图网站的所有表情包
  • JS字符串转数字方法总结
  • Making An Indicator With Pure CSS
  • mysql中InnoDB引擎中页的概念
  • niucms就是以城市为分割单位,在上面 小区/乡村/同城论坛+58+团购
  • Otto开发初探——微服务依赖管理新利器
  • Terraform入门 - 1. 安装Terraform
  • unity如何实现一个固定宽度的orthagraphic相机
  • 关于字符编码你应该知道的事情
  • 实战:基于Spring Boot快速开发RESTful风格API接口
  • 使用前端开发工具包WijmoJS - 创建自定义DropDownTree控件(包含源代码)
  • 数据库写操作弃用“SELECT ... FOR UPDATE”解决方案
  • 学习笔记DL002:AI、机器学习、表示学习、深度学习,第一次大衰退
  • 优化 Vue 项目编译文件大小
  • ionic异常记录
  • 大数据全解:定义、价值及挑战
  • 湖北分布式智能数据采集方法有哪些?
  • ​决定德拉瓦州地区版图的关键历史事件
  • #android不同版本废弃api,新api。
  • #LLM入门|Prompt#1.8_聊天机器人_Chatbot
  • (DenseNet)Densely Connected Convolutional Networks--Gao Huang
  • (Matlab)使用竞争神经网络实现数据聚类
  • (二)WCF的Binding模型
  • (附表设计)不是我吹!超级全面的权限系统设计方案面世了
  • (附源码)计算机毕业设计高校学生选课系统
  • (十五)devops持续集成开发——jenkins流水线构建策略配置及触发器的使用
  • (一)RocketMQ初步认识
  • (转)Linux整合apache和tomcat构建Web服务器
  • (转)linux自定义开机启动服务和chkconfig使用方法
  • **PyTorch月学习计划 - 第一周;第6-7天: 自动梯度(Autograd)**
  • ./configure、make、make install 命令
  • .bat批处理(十一):替换字符串中包含百分号%的子串
  • .NET Framework与.NET Framework SDK有什么不同?
  • .net 受管制代码
  • .Net6 Api Swagger配置