• 教你一步一步实现一个Promise


    Promise我想现在大家都非常熟悉了,主要作用就是解决异步回调问题,这里简单介绍下。

    Promise规范是CommonJS规范之一,而Promise规范又分了好多种,比如 Promises/A、Promises/B、Promises/Kiss等等

    有兴趣的可以到这多了解一些 http://wiki.commonjs.org/wiki/Promises

    现在比较流行的是Promise/A规范,人们对它的完善和扩展,逐渐形成了Promise/A+规范,A+已脱颖而出。

    说到这里规范是什么,可以去这里了解下

    http://promises-aplus.github.io/promises-spec/

    http://hussion.me/2013/10/19/promises-a/

    现在已有浏览器内置支持Promise,它的api语法可以在这里查看

    http://www.html5rocks.com/zh/tutorials/es6/promises/#toc-api

    可以看到它的api并不多,其实规范也不多,我觉的大致抓住几个重要的点就够了,

    1、promise有三种状态,等待(pending)、已完成(fulfilled)、已拒绝(rejected)

    2、promise的状态只能从“等待”转到“完成”或者“拒绝”,不能逆向转换,同时“完成”和“拒绝”也不能相互转换

    3、promise必须有一个then方法,而且要返回一个promise,供then的链式调用,也就是可thenable的

    4、then接受俩个回调(成功与拒绝),在相应的状态转变时触发,回调可返回promise,等待此promise被resolved后,继续触发then链

    知道这几个重要的特点,我们就可以参考浏览器内置的api来实现了,

    我们可以不必太受规范约束,先按照自己的想法来就好了。

    promise的使用大致如下

    var promise = new Promise(function(resolve, reject) {
    	setTimeout(function(){
    		resolve('val')
    	});
    });
    
    promise.then(onFulfilled,onRejected).then(onFulfilled,onRejected)

    主要思路就是我们可以直接对返回的promise对象进行操作,比如then,传入回调,

    这里的函数并不会立即执行,而是加入队列,等待未来的某个时间resolve时被触发执行。

    有了以上说明,就可以来实现了

    首先定义三个状态

    var PENDING = undefined, FULFILLED = 1, REJECTED = 2;

    然后实现Promise构造函数,此函数接受一个函数参数,函数参数接受俩个我们提供的方法resolve与reject,

    供使用者在未来的某个时间里调用触发执行我们的队列,这里还要初始下当前的状态,传递的值,

    以及then时保存到的队列。

    大概像下面这样

    var Promise = function(resolver){
        if (!isFunction(resolver))
            throw new TypeError('You must pass a resolver function as the first argument to the promise constructor');
        if(!(this instanceof Promise)) return new Promise(resolver);
    
        var promise = this;
        promise._value;
        promise._reason;
        promise._status = PENDING;
        promise._resolves = [];
        promise._rejects = [];
        
        var resolve = function(value){
            //状态转换为FULFILLED 
            //执行then时保存到_resolves里的回调,
            //如果回调有返回值,更新当前_value
        }
        var reject = function(reason){
            //状态转换为REJECTED
            //执行then时保存到_rejects里的回调,
            //如果回调有返回值,更新当前_rejects
        }
        
        resolver(resolve,reject);
    }

    有了这个,我们在实现一个then就ok了,

    then里要做的就是返回一个promise供then的链式调用,

    而且promise.then(onFulfilled,onRejected)时,我们要判断当前promise的状态,

    如果是pending则把onFulfilled与onRejected添加到_resolves与_rejects数组里,

    否则的话根据状态,直接触发回调,这里要注意的是,如果返回的是promise,我们要等到此promise被resolves时,

    触发then链的下一个promise执行。

    代码大概是这样

    Promise.prototype.then = function(onFulfilled,onRejected){
        var promise = this;
        // 每次返回一个promise,保证是可thenable的
        return Promise(function(resolve,reject){
            function callback(value){
              var ret = isFunction(onFulfilled) && onFulfilled(value) || value;
              if(isThenable(ret)){
                  // 根据返回的promise执行的结果,触发下一个promise相应的状态
                ret.then(function(value){
                   resolve(value); 
                },function(reason){
                   reject(reason);
                });
              }else{
                resolve(ret);
              }
            }
            function errback(reason){
                reason = isFunction(onRejected) && onRejected(reason) || reason;
                reject(reason);
            }
            if(promise._status === PENDING){
                   promise._resolves.push(callback);
                   promise._rejects.push(errback);
               }else if(promise._status === FULFILLED){ // 状态改变后的then操作,立刻执行
                   callback(promise._value);
               }else if(promise._status === REJECTED){
                   errback(promise._reason);
               }
        });
    }

    这里说明下

    var isThenable = function(obj){
      return obj && typeof obj['then'] == 'function';
    }

    也就是说返回的对象带有then方法,我们就当作promise对象

    到这里我们主要的工作就完成了,其他的all,race等方法都很简单,具体可以到这里看完整的实现

    https://github.com/ygm125/promise/blob/master/promise.js

    下面我们来做几个例子来看下效果

    var getData100 = function(){
        return Promise(function(resolve,reject){
            setTimeout(function(){
                resolve('100ms');
            },100);
        });
    }
    
    var getData200 = function(){
        return Promise(function(resolve,reject){
            setTimeout(function(){
                resolve('200ms');
            },200);
        });
    }
    
    getData100().then(function(data){
        console.log(data); // 100ms
        return getData200();
    }).then(function(data){
        console.log(data); // 200ms
        return data + data;
    }).then(function(data){
        console.log(data) // 200ms200ms
    });

    当然可以直接getData100().then(getData200).then(function(val){})

    then可以只传一个,接受成功的回调,也可以用catch方法,接受失败的回调,

    catch是then的一个语法糖,相当于promise.then(undefined, onRejected)

    也可以用all来并行执行

    Promise.all([getData100(),getData200()]).then(function(value){
        console.log(value) // ['100ms','200ms']
    });

    结果的顺序与传入的顺序相同。

    我们也可以直接创建一个以obj为值的成功状态的promise,

    Promise.resolve('FULFILLED').then(function(val){
        console.log(val) // FULFILLED
    });

    实现都相当简单,看代码就懂。

    这里也可以做一些好玩的,比如创建一个delay方法

    Promise.prototype.delay = function(ms){
        return this.then(function(val){
            return Promise.delay(ms,val);
        })
    }
    
    Promise.delay = function(ms,val){
        return Promise(function(resolve,reject){
            setTimeout(function(){
                resolve(val);
            },ms);
        })
    }

    我们可以每隔多少毫秒执行一些操作

    Promise.delay(1000).then(function(){
        // 一些操作
    }).delay(1000).then(function(){
        // 一些操作
    })

    我们也可以包装一个循环,执行多少次,每次延迟多少秒执行什么操作


    var len = 0,
        words = '你好,你是谁?';
    
    function count(num,ms,cb){
        var pro = Promise.resolve();
        for (var i = 0; i < num; i++) {
            pro = pro.delay(ms).then(function(v){
                return cb(v);
            });
        };
    }
    
    count(words.length,800,function(){
        var w = words.substr(0,++len);
        console.log(w);
    })

    更多的东西等你来实现~ 

  • 相关阅读:
    (4)Maven快速入门_4在Spring+SpringMVC+MyBatis+Oracle+Maven框架整合运行在Tomcat8中
    (3)Maven快速入门_3在Eclipse中创建Maven项目打包成jar
    (2)Maven快速入门_2maven在Eclipse中的设置
    (1)Maven快速入门_1maven安装
    (11)Microsoft office Word 2013版本操作入门_word中表格操作
    (10)Microsoft office Word 2013版本操作入门_word表格
    洛谷 P2144 [FJOI2007]轮状病毒
    洛谷 P2234 [HNOI2002]营业额统计
    【模板】主席树
    洛谷 P2572 [SCOI2010]序列操作
  • 原文地址:https://www.cnblogs.com/ygm125/p/3735677.html
Copyright © 2020-2023  润新知