已掉线,重新登录

首页 > 绿虎论坛 > 历史版块 > 编程 > HTML/CSS/JS

标题: 异步Ajax:返回(Return)、延迟(Deferred)与承诺(Promise)

作者: @Ta

时间: 2016-11-06发布,2016-11-06修改

点击: 3791

Promise是一种全新的技术,它可以让你在浏览器中进行异步ajax调用时同步的返回(Return)调用结果。

在此之前,你根本不可能在“不造成网页卡顿”的前提下安全的返回ajax的结果,因为为了返回结果,你必须关闭ajax的异步模式使其进入同步模式。但是,一但你在浏览器用于渲染网页的“主线程”中进入同步模式的ajax,整个标签页除了等待ajax完成就再也做不了任何事了,也就是说整个标签页都会卡住,这对用户体验来说是及其恶劣的。

但是,我们又想返回ajax调用结果,因为我们不想陷入传统的回调函数地狱。所以有了Promise。我会通过一个例子直观的展示什么是Promise。

以下代码可以在网页插件中测试

-------------------------------------

首先是并行两个Ajax请求:

<script src="//hu60.cn/tpl/classic/js/jquery/dist/jquery.min.js"></script>
<script>
//getName函数把虎绿林用户的uid转换为用户名
function getName(uid) {
    //Deferred的意思是“延迟”
    //这个result虽然会马上返回,但是只有在未来才会得到结果
    var result = $.Deferred();

    //$.get()返回的也是一个“延迟”对象
    //不过这个“延迟”对象是只读的,我们通常称为“Promise”(承诺)
    //关于“Promise”的更多细节看return语句的注释
    var ajax = $.get('http://hu60.cn/q.php/api.user.json?type=uid2name&uid=' + uid);
    
    //当 result.promise() 被返回的时候,这些代码还没有执行
    //只有在ajax请求成功之后,这些代码才会执行
    ajax.done(function(json) {
        if (!json.error && json.name != null) {
            //这是真正“return”结果的地方
            //resolve的意思是“解决”
            //这是在宣告,我原本给你的“承诺”兑现了,看,json.name就是我想给你的结果
            result.resolve(json.name);
        } else {
            //reject的意思是“背弃”
            //这是在宣布,我“背弃”了原本“要给你一个结果”的“承诺”,现在这个“承诺”永远不会有结果了
            result.reject();
        }
    });

    //ajax请求失败的时候执行
    ajax.fail(function() {
        //ajax都没成功,自然要“背弃承诺”
        result.reject();
    });

    //promise()方法得到一个只读的“延迟”对象
    //所谓“只读”,就是拿到这个对象的人只能等待结果,不能改变这个对象的状态
    //所以我们把只读的“延迟”对象叫做Promise(承诺)
    //你可以返回给别人一个“承诺”,告诉他:虽然结果不会马上出来,但是我最后一定会给你一个结果
    //而我们改变result对象的状态的时候,它的promise对象的状态也会跟着改变。
    return result.promise();
}

//我得到的不是一个用户名,而是一个对用户名的承诺
var result = getName(1);

//承诺兑现了,拿到用户名
result.done(function(name) {
    alert('uid(1):' + name);
});

//承诺没有兑现
result.fail(function() {
    alert('获取用户名失败');
});
    
//另一个承诺
var result2 = getName(0);

//兑现了
result2.done(function(name) {
    alert('uid(0):' + name);
});

//没有兑现
result2.fail(function() {
    alert('获取用户名失败');
});
</script>


在这个例子中,result1会被兑现(得到老虎会游泳),result2会被背弃(因为uid0的name是null)。

你们运行这个代码的时候可能会发现,其实result1和result2的兑现顺序是不固定的。

刷新几次你会发现,有时候“获取用户名失败”先出现,有时候“老虎会游泳”先出现。

我们有理由相信在浏览器中两个ajax请求是同时在执行的。

-------------------------------------

然后是依次执行两个请求,并且在失败的时候返回错误代码:

<script src="//hu60.cn/tpl/classic/js/jquery/dist/jquery.min.js"></script>
<script>
//getName函数把虎绿林用户的uid转换为用户名
function getName(uid) {
    //Deferred的意思是“延迟”
    //这个result虽然会马上返回,但是只有在未来才会得到结果
    var result = $.Deferred();

    //$.get()返回的也是一个“延迟”对象
    //不过这个“延迟”对象是只读的,我们通常称为“Promise”(承诺)
    //关于“Promise”的更多细节看return语句的注释
    var ajax = $.get('http://hu60.cn/q.php/api.user.json?type=uid2name&uid=' + uid);
    
    //当 result.promise() 被返回的时候,这些代码还没有执行
    //只有在ajax请求成功之后,这些代码才会执行
    ajax.done(function(json) {
        if (!json.error && json.name != null) {
            //这是真正“return”结果的地方
            //resolve的意思是“解决”
            //这是在宣告,我原本给你的“承诺”兑现了,看,json.name就是我想给你的结果
            result.resolve(json.name);
        } else {
            //reject的意思是“背弃”
            //这是在宣布,我“背弃”了原本“要给你一个结果”的“承诺”,现在这个“承诺”永远不会有结果了
            result.reject();
        }
    });

    //ajax请求失败的时候执行
    ajax.fail(function() {
        //ajax都没成功,自然要“背弃承诺”
        result.reject();
    });

    //promise()方法得到一个只读的“延迟”对象
    //所谓“只读”,就是拿到这个对象的人只能等待结果,不能改变这个对象的状态
    //所以我们把只读的“延迟”对象叫做Promise(承诺)
    //你可以返回给别人一个“承诺”,告诉他:虽然结果不会马上出来,但是我最后一定会给你一个结果
    //而我们改变result对象的状态的时候,它的promise对象的状态也会跟着改变。
    return result.promise();
}

//getName2()获得uid1和uid2的用户名,并且连接成字符串“返回”
//如果请求失败,“返回”错误代码-1或者-2
function getName2() {
    var uid1 = 1; //可以改成0,看name1失败的效果
    var uid2 = 2; //可以改成0,看name2失败的效果

    var result = $.Deferred();

    //先获取name1
    var name1p = getName(uid1);

    //对应同步的风格 if (name1p != null) {...}
    name1p.done(function(name1) {
        //name1有了,再获取name2
        var name2p = getName(uid2);

        //对应同步的风格 if (name2p != null) {...}
        name2p.done(function(name2) {
            //name2有了,现在可以做我们想做的事情了
            var nameStr = '1:' + name1 + ', 2:' + name2;
            //对应同步的风格 return ...
            result.resolve(nameStr);
        });
        
        //对应同步的风格 else {...}
        name2p.fail(function() {
            //对应同步的风格 return -2;
            result.resolve(-2);
        });
    });

    //对应同步的风格 else {...}
    name1p.fail(function() {
        //对应同步的风格 return -1
        result.resolve(-1);
    });

    //这是例行的返回一个承诺
    //上面的done和fail里的代码都不会立即运行
    //相反,这句代码会立即运行,也就是说,函数马上就返回了
    return result.promise();
}


var msgP = getName2();

//对应同步的风格 if (msgP != null) {...}
msgP.done(function(msg) {
    if (msg == -2) {
        alert('name2失败');
    } else if (msg == -1) {
        alert('name1失败');
    } else {
        alert(msg);
    }
});

//对应同步的风格 else {...}
msgP.fail(function() {
    console.err('失败');
});

//同样,这句代码也会最先执行
alert('Promise是非阻塞的,所以这个提示框是最先出现的');
</script>

[隐藏样式|查看源码]


『回复列表(4|隐藏机器人聊天)』

1. 因缺思厅
(/@Ta/2016-11-06 01:21//)

2. 硬却失挺
(/@Ta/2016-11-06 07:35//)

3. 老虎开始学JavaScript了?
(/@Ta/2016-11-06 08:23//)

4. @大药瓶子,“ 添加附件 ”~
(/@Ta/2016-11-06 12:53//)

回复需要登录

9月17日 05:08 星期三

本站由hu60wap6驱动

备案号: 京ICP备18041936号-1