异步Promise实现

简介 

  异步回调的书写往往打乱了正常流的书写方式,在ECMAScript 6中实现了标准的Promise API,旨在

解决控制回调流程的问题。

  简单的实现了Promise API: 

  1       (function(w){
  2             function Promise(fn){
  3                 return this instanceof Promise ? this.init(fn) : new Promise(fn);
  4             }
  5             Promise.fulfill = function(m){return m;};
  6             Promise.reject = function(m){throw m;};
  7             Promise.map = {
  8                 resolve: "onFulfill",
  9                 reject: "onReject"
 10             }
 11             //异步自动生成promise并执行
 12             Promise.resolve = function(fn){
 13                 var p = new Promise();
 14                 setTimeout(function(){
 15                     p.resolve();
 16                 },0);
 17                 if(fn)
 18                     p.callback["onFulfill"] = fn;
 19                 return p;
 20             };
 21             Promise.all = function(){
 22                 var p = new Promise(),
 23                         args;
 24                 var counter = 0,ret = [];//收集结果,并传给p
 25                 var v,fn; //传入的函数,执行该函数,将结果保存至ret
 26                 if(arguments.length > 1){
 27                     args = [].slice.apply(arguments)
 28                 }else if({}.toString.call(arguments[0]) == "[object Array]"){
 29                     args = arguments[0];
 30                 }
 31                 for(var i=0,len=args.length;i<len;i++){
 32                     if(typeof args[i] == "function"){
 33                         args[i] = Promise.resolve(args[i]);
 34                     }
 35 
 36                     (function(i){
 37                         args[i].then(function(m){
 38                             ret.push(m);
 39                             if(--counter <= 0){
 40                                 ret.length = len;
 41                                 p.resolve(ret);
 42                             }
 43                         },function(){
 44                             p.reject();
 45                         });
 46                     })(i)
 47                     counter++;
 48                 }
 49                 return p;
 50             };
 51             Promise.prototype = {
 52                 init: function(fn){
 53                     var that = this;
 54                     this.state = 'pending';
 55                     this.callback = {
 56                         onFulfill: Promise.fulfill,
 57                         onReject: Promise.reject
 58                     };
 59                     this.dirty = false;
 60                     this._next = null;
 61                     setTimeout(function(){
 62                         fn && fn.call(that,that.resolve.bind(that),that.reject.bind(that));
 63                     },0)
 64 
 65                 },
 66                 then: function(onFulfill,onReject){
 67                     return post.call(this,onFulfill,onReject);
 68                 },
 69                 wait: function(mills){ //promise链在wait处被分裂成2段
 70                     var p = new Promise(),
 71                             start = new Date().getTime();
 72                     var id = setTimeout(function(){ //传入时间
 73                         p.resolve([this.val,new Date().getTime() - start])
 74                     },mills);
 75                     p.cancel = function(){
 76                         clearTimeout(id);
 77                     }
 78                     return p;
 79                 }
 80             }
 81             function post(onFulfill,onReject,onNotify,onComplete){
 82                 var p = new Promise(),
 83                         that = this;
 84                 if(arguments.length <= 2){
 85                     that._next = p;
 86                     that.callback["onFulfill"] = onFulfill;
 87                     that.callback["onReject"] = onReject;
 88                     this.dirty = true;
 89                 }
 90                 return p;
 91             }
 92             function fire(promise,method){
 93                 var next = "resolve",val,
 94                         args = arguments[2];
 95                 if(promise.state == "pending"){
 96                     try{
 97                         promise.val = val = promise.callback[Promise.map[method]].apply(promise,args);
 98                         promise.state = method;
 99                     }catch(e){
100                         promise.val = val = e;
101                         next = "reject";
102                     }
103 
104                     if(val && isPromise(val)){
105                         val._next = promise._next;
106                     }else{
107                         if(promise._next){
108                             fire(promise._next,next,[val]);
109                         }
110                     }
111 
112                 }
113                 return promise;
114             }
115             function isPromise(o){
116                 return o && typeof o == "object" && o.then && typeof o.then == "function";
117             }
118             "reject,resolve".replace(/\w+/g,function(m){
119                 Promise.prototype[m] = function(){
120                     return fire(this,m,arguments);
121                 }
122             })
123 
124             w.Promise = Promise;
125         })(window)

 

 

 

示例

  示例内容为依次加载网页内容的各个元素:先加载标题,并根据服务器返回的url信息,到相应的文件中加载

内容,并以此显示。

 1       var getJson = function(url){
 2             return new Promise(function(resolve,reject){
 3                 var that = this;
 4                 var xhr = new XMLHttpRequest();
 5                 if(!window.Promise)return;
 6                 xhr.open('get',url);
 7                 xhr.onreadystatechange = function(e){
 8                     if(xhr.readyState == 4){
 9                         if(xhr.status >= 200 && xhr.status < 300 || xhr.status == 304){
10                             resolve(xhr.responseText); log(that)
11                         }else{
12                             reject(new Error('response error'));
13                         }
14                     }
15                 };
16                 xhr.onerror = function(e){
17                     reject(new Error('ajax error'));
18                 }
19                 xhr.send();
20             });
21         };
22 
23         var body = document.body;
24         var addHtml = function(html){
25             if(typeof  html != 'string') return;
26             var p = document.createElement('p');
27             p.textContent = html;
28             body.insertBefore(p,loading);
29         };
30         var addHead = function(html){
31             if(typeof html !== 'string') return;
32             var h = document.createElement('h2');
33             h.textContent = html;
34             body.insertBefore(h,loading);
35         }
36         var log = function(msg){console.log(msg)};
37         var loading = document.getElementById('loading');
38 
39          getJson('../json/head.json').then(JSON.parse).then(function(html){
40             addHead(html.content);
41             Promise.all(html.urls.map(getJson)).then(function(arr){
42                 arr.forEach(function(content){
43                     addHtml(JSON.parse(content).content);
44                 })
45          },function(e){
46                 log('error in loading content: '+ e);
47            })
48          },function(e){
49              log('error: ' + e);
50          }).then(function(){
51             loading.style.display = 'none';
52          })
53 
54         getJson('../json/head.json').then(JSON.parse).then(function(html){
55             addHead(html.content);
56             var promise = Promise.resolve();
57             html.urls.forEach(function(url,i){
58                 promise = promise.then(function(){
59                     return getJson(url);
60                 }).then(JSON.parse).then(function(html){
61                     addHtml(html.content);
62                 },function(e){
63                     log('error in loading body: '+ e );
64                 }).then(function(){
65                     if(i == html.urls.length-1)
66                         loading.style.display = 'none';
67                 })
68             })
69         })

 

 

 

 

示范

  Promise API控制流程,尤其是对于异步操作而言,流程非常清晰,开飞相对容易。

posted @ 2015-01-13 20:55  royalrover  阅读(1422)  评论(0编辑  收藏  举报