异步回调的书写往往打乱了正常流的书写方式,在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控制流程,尤其是对于异步操作而言,流程非常清晰,开飞相对容易。