rwson

rwson

一个前端开发

ES6中的Promise

在执行一些异步操作(典型的有JavaScript中的ajax/NodeJs中读取文件等等)的时候,我们不知道该操作什么时候完成,所以就需要在不同的时候写上回调,等到有返回的时候,再执行下一步操作,下面就用jQuery中的一个ajax来做示例:

$.ajax({
    "url": "xxx",
    "type": "GET",
    "dataType": ""JSON,
    "success": function(res){},
    "error": function(ex){
        //  do some thing
    }
});

最基础的一个ajax示例,当我们有多个ajax嵌套请求的时候,就中了所谓的"回调地狱",类似于下面的写法:

$.ajax({
    //  some configs
    "success": function(res){
        $.ajax({
            //  ...
            "success": function(res){
                $.ajax({
                    //  ...
                    "success":function(res){
                        .
                        .
                        .
                    }
                });
            }
        });
    }
});

一层套着一层,代码可读性很差,且不容易后期的维护

这时候就需要一个比前者好的解决方案来解决该问题,ES6中的Promise一定程度上解决了该问题:

我们可以利用Promise对ajax进行一层封装

function _ajax(url, method, args) {
    let promise = new Promise((resolve, reject) => {

        let client = new XMLHttpRequest();
        let uri = url;

        if (args && (method == "POST" || method == "PUT")) {
            let argcount = 0;
            uri += "?";
            for (var key in args) {
                if (args.hasOwnProperty(key)) {
                    if (argcount++) {
                        uri += '&';
                    }
                    uri += encodeURIComponent(key) + '=' + encodeURIComponent(args[key]);
                }
            }
        }

        client.open(method, uri);
        client.send();

        client.onload = function() {
            if (this.status >= 200 && this.status < 300) {
                resolve(this.response);
            } else {
                reject(this.statusText);
            }
        };
        client.onerror = function() {
            reject(this.statusText);
        };

    });

    return promise;
}

let core = {

    "GET": function(args) {
        return _ajax(url, "GET", args);
    },

    "POST": function(args) {
        return _ajax(url, "POST", args);
    },

    "PUT": function(args) {
        return _ajax(url, "PUT", args);
    },

    "DELETE": function(args) {
        return _ajax(url, "DELETE", args);
    }

};

上面这段代码是JavaScript MDN上的代码(可能稍微有点改动),它对ajax进行了一层封装,经过这层封装,我们可以像下面这样写一些异步代码:

$http("xxx")
.GET()
.then((data) => {
    
    //  do something
    
    $http("xxx2")
    .GET()
    .then((data) => {
    
        //  do something
        
        $http("xxx3")
        .GET({"key","value"})
        .then((data) => {
            //  do something...
        })
        
        
    },(ex) => {});
    
})
.catch((ex) => {});

虽然还有嵌套,但是代码看起来已经舒服了很多。

Promise是一个异步编程的抽象,它是一个返回值或抛出exception的代理对象,一般promise对象都有一个then方法,这个then方法是我们如何获得返回值(成功实现承诺的结果值,称为fulfillment)或抛出exception(拒绝承诺的理由,称为rejection),then是用两个可选的回调作为参数,我们可以称为onFulfilled和OnRejected,也可以把OnRejected写在catch里面

所以一个Promise一共有下面几个状态

  1. pending待承诺 - promise初始状态
  2. fulfilled实现承诺 - 一个承诺成功实现状态
  3. rejected拒绝承诺 - 一个承诺失败的状态

再来个NodeJs中读取文件的例子:

function readFile(path) {
    var fs = require("fs");
    var prromise = new Promise((resolve, reject) => {
        fs.readdir(path, (ex, files) => {
            if (ex) {
                return reject(ex);
            }
            return resolve(files);
        });
    });
    
    return prromise;
}

readFile(config.avatarPath + "1").then((files) => {
    //  do some thing
    console.log(files);
}).catch((ex) => {
    //  do something...
    console.log(ex);
});

如果不用Promise和一些ES6的特性,上面的代码应该看起来是下面的这样子:

var fs = require("fs");
fs.readdir(path, function (ex, files) {
    if (ex) {
        //  do something
        return console.log();
    }
    
    //  do some thing
    console.log(files);
});

代码量可能更少,但是陷入"回调地狱"的可能就更大了,在ES7中,又新增了async/await特性来针对异步操作的,后面介绍😄