Promise

Promise

Promise已经获得所有现代浏览器的支持,是时候使用它来重构你的代码了。

Promise可以改变我们代码的书写方式,让代码的逻辑和结构更加清晰,帮助你从回调地狱中解脱。

回调模式

下面是一个简单的例子,如果你还不知道它是用来干什么的话。

function get(url,callback){
            var xhr = new XMLHttpRequest();
            xhr.open('GET',url,true);
            xhr.onload=function(){
                if(xhr.status===200){
                    callback(null,xhr.response);
                }else{
                    callback(Error(xhr.statusText));
                }
            };
            xhr.onerror=function(){
                callback(Error("Network Error!"));
            };
            xhr.send();
    }
function log(err,data){
    if(err) return console.log(err);
    console.log(data)
}

get("someurl...",log);

上面的函数通过ajax请求获取指定url返回的数据,并且在控制台打印出来。因为要在ajax请求成功,并且返回数据之后,我们才能执行log函数,所以,按照习惯,我们使用了回调函数。

现在,如果有一个新的需求,我们在log函数执行之后,还要执行另一个操作,比如说将ajax请求返回的结果进行一系列的处理。

function handleData(data){
    ......
}

按照常规的想法,我们需要把这个函数当作回调传入到log函数中,这样,当打印完毕后,再执行handleData。那么,log函数需要做一些修改:

function log(err,data,callback){
    if(err) return console.log(err);
    console.log(data);
    callback && callback(data);
}

由于log函数是作为回调传入get函数中的,log函数自身也有一个函数参数作为回调,我们需要在get函数中传入它。

    function get(url, callback, logcallback) {
        var xhr = new XMLHttpRequest();
        xhr.open('GET', url, true);
        xhr.onload = function() {
            if (xhr.status === 200) {
                callback(null, xhr.response, logcallback);
            } else {
                callback(Error(xhr.statusText), null, logcallback);
            }
        };
        xhr.onerror = function() {
            callback(Error("Network Error!"));
        };
        xhr.send();
    }

现在我们的流程设计完毕,开始调用:

get("someurl",log,handleData);

如果我们的数据需要经过一系列的处理,那么这个回调参数的个数会越来越多。

使用Promise

同样的需求使用Promise来解决,代码如下:

get("someurl").then(log).then(handleData);

代码是不是变简洁而又清晰?不过,在实现这样的代码之前,我们要对以上的函数做出一些修改,让他们华丽的变身。

首先,我们要修改get函数:

function get(url){
        var promise = new Promise(function(resolve,reject){
            var xhr = new XMLHttpRequest();
            xhr.open('GET',url,true);
            xhr.onload=function(){
                if(xhr.status===200){
                    resolve(xhr.response);
                }else{
                    reject(Error(xhr.statusText));
                }
            };
            xhr.onerror=function(){
                reject(Error("Network Error!"));
            };
            xhr.send();
        });
        return promise;
    }

可以看到,get函数现在返回了一个promise对象。而promise对象具有then方法,所以我们可以get函数返回promise之后,直接调用它的then方法,then方法也返回一个promise对象,所以可以继续调用then方法,如果这方面你还不是很懂的话,想想jquery的链式调用吧!

promise对象的生成

在这里我们使用的是最基本的方法,通过构造函数:

var promise = new Promise(function(resolve,reject){
    ......
});

省略掉的部分就是你原本的代码逻辑,唯一不同的一点,就是对于返回值的处理。

对于ajax请求来说,有两种状态:请求成功,请求失败。 当请求成功时,我们把数据返回,不过这里用resolve 代替return。

resolve(xhr.response);

当请求失败时,我们把错误返回,这里用reject代替return。

reject(xhr.statusText);

我们暂且不考虑其他情况。

resolve 和 reject

它们代替了我们平时所使用的return,这样做的目的是什么?

我们知道,ajax请求是需要花费时间的。我们要等待请求并返回结果。 当结果返回时,不管是数据或者错误,这个请求都是确确实实的发生并且完成了。而resolve和reject就是把这个promise对象的状态进行改变,由等待状态改变至完成状态。

如果都是把promise对象的状态改变为完成状态的话,为什么要用两个不同的名称呢,都用resolve不就行了嘛?

我们知道,ajax请求完成了,但是完成也有两种状态。一种是成功获取到数据的完成,一种是请求出错的完成。但是这两种完成都是完成,都经历了发送请求,等待响应,返回结果的过程。

当以resolve方式改变promise对象的状态至完成状态时,会调用后续的then方法中第一个参数,做为回调。 当以reject方式改变promise对象的状态至完成状态时,调用then方法中第二个参数作为回调,或者catch方法中的参数作为回调。

get("someurl").then(log,logerr);    
get("someurl").then(log).catch(logerr);
posted @ 2016-04-21 11:56  杯酒红尘  阅读(314)  评论(0编辑  收藏  举报