在JavaScript的世界中,所有代码都是单线程执行的。

由于这个“缺陷”,导致JavaScript的所有网络操作,浏览器事件,都必须是异步执行。异步执行可以用回调函数实现:
function callback() {
console.log('Done');
}
console.log('before setTimeout()');
setTimeout(callback, 1000); // 1秒钟后调用callback函数
console.log('after setTimeout()');
观察上述代码执行,在Chrome的控制台输出可以看到:
before setTimeout()
after setTimeout()
(等待1秒后)
Done
可见,异步操作会在将来的某个时间点触发一个函数调用。
AJAX就是典型的异步操作。以上一节的代码为例:
request.onreadystatechange = function () {
if (request.readyState === 4) {
if (request.status === 200) {
return success(request.responseText);
} else {
return fail(request.status);
}
}
}
把回调函数success(request.responseText)和fail(request.status)写到一个AJAX操作里很正常,但是不好看,而且不利于代码复用。
有没有更好的写法?比如写成这样:
var ajax = ajaxGet('http://...');
ajax.ifSuccess(success)
.ifFail(fail);
这种链式写法的好处在于,先统一执行AJAX逻辑,不关心如何处理结果,然后,根据结果是成功还是失败,在将来的某个时候调用success函数或fail函数。
古人云:“君子一诺千金”,这种“承诺将来会执行”的对象在JavaScript中称为Promise对象。
Promise有各种开源实现,在ES6中被统一规范,由浏览器直接支持。先测试一下你的浏览器是否支持Promise:
'use strict';
new Promise(function () {});
// 直接运行测试:
alert('支持Promise!');
我们先看一个最简单的Promise例子:生成一个0-2之间的随机数,如果小于1,则等待一段时间后返回成功,否则返回失败:
function test(resolve, reject) {
var timeOut = Math.random() * 2;
log('set timeout to: ' + timeOut + ' seconds.');
setTimeout(function () {
if (timeOut < 1) {
log('call resolve()...');
resolve('200 OK');
}
else {
log('call reject()...');
reject('timeout in ' + timeOut + ' seconds.');
}
}, timeOut * 1000);
}
这个test()函数有两个参数,这两个参数都是函数,如果执行成功,我们将调用resolve('200 OK'),如果执行失败,我们将调用reject('timeout in ' + timeOut + ' seconds.')。可以看出,test()函数只关心自身的逻辑,并不关心具体的resolve和reject将如何处理结果。
有了执行函数,我们就可以用一个Promise对象来执行它,并在将来某个时刻获得成功或失败的结果:
var p1 = new Promise(test);
var p2 = p1.then(function (result) {
console.log('成功:' + result);
});
var p3 = p2.catch(function (reason) {
console.log('失败:' + reason);
});
变量p1是一个Promise对象,它负责执行test函数。由于test函数在内部是异步执行的,当test函数执行成功时,我们告诉Promise对象:
// 如果成功,执行这个函数:
p1.then(function (result) {
console.log('成功:' + result);
});
当test函数执行失败时,我们告诉Promise对象:
p2.catch(function (reason) {
console.log('失败:' + reason);
});
Promise对象可以串联起来,所以上述代码可以简化为:
new Promise(test).then(function (result) {
console.log('成功:' + result);
}).catch(function (reason) {
console.log('失败:' + reason);
});
实际测试一下,看看Promise是如何异步执行的:
'use strict';
// 清除log:
var logging = document.getElementById('test-promise-log');
while (logging.children.length > 1) {
logging.removeChild(logging.children[logging.children.length - 1]);
}
// 输出log到页面:
function log(s) {
var p = document.createElement('p');
p.innerHTML = s;
logging.appendChild(p);
}
new Promise(function (resolve, reject) {
log('start new Promise...');
var timeOut = Math.random() * 2;
log('set timeout to: ' + timeOut + ' seconds.');
setTimeout(function () {
if (timeOut < 1) {
log('call resolve()...');
resolve('200 OK');
}
else {
log('call reject()...');
reject('timeout in ' + timeOut + ' seconds.');
}
}, timeOut * 1000);
}).then(function (r) {
log('Done: ' + r);
}).catch(function (reason) {
log('Failed: ' + reason);
});
Log:
start new Promise...
set timeout to: 0.5354042750614991 seconds.
call resolve()...
Done: 200 OK
可见Promise最大的好处是在异步执行的流程中,把执行代码和处理结果的代码清晰地分离了:
Promise还可以做更多的事情,比如,有若干个异步任务,需要先做任务1,如果成功后再做任务2,任何任务失败则不再继续并执行错误处理函数。
要串行执行这样的异步任务,不用Promise需要写一层一层的嵌套代码。有了Promise,我们只需要简单地写:
job1.then(job2).then(job3).catch(handleError);
其中,job1、job2和job3都是Promise对象。
下面的例子演示了如何串行执行一系列需要异步计算获得结果的任务:
'use strict';
var logging = document.getElementById('test-promise2-log');
while (logging.children.length > 1) {
logging.removeChild(logging.children[logging.children.length - 1]);
}
function log(s) {
var p = document.createElement('p');
p.innerHTML = s;
logging.appendChild(p);
}
// 0.5秒后返回input*input的计算结果:
function multiply(input) {
return new Promise(function (resolve, reject) {
log('calculating ' + input + ' x ' + input + '...');
setTimeout(resolve, 500, input * input);
});
}
// 0.5秒后返回input+input的计算结果:
function add(input) {
return new Promise(function (resolve, reject) {
log('calculating ' + input + ' + ' + input + '...');
setTimeout(resolve, 500, input + input);
});
}
var p = new Promise(function (resolve, reject) {
log('start new Promise...');
resolve(123);
});
p.then(multiply)
.then(add)
.then(multiply)
.then(add)
.then(function (result) {
log('Got value: ' + result);
});
Log:
start new Promise...
calculating 123 x 123...
calculating 15129 + 15129...
calculating 30258 x 30258...
calculating 915546564 + 915546564...
Got value: 1831093128
setTimeout可以看成一个模拟网络等异步执行的函数。现在,我们把上一节的AJAX异步执行函数转换为Promise对象,看看用Promise如何简化异步处理:
'use strict';
// ajax函数将返回Promise对象:
function ajax(method, url, data) {
var request = new XMLHttpRequest();
return new Promise(function (resolve, reject) {
request.onreadystatechange = function () {
if (request.readyState === 4) {
if (request.status === 200) {
resolve(request.responseText);
} else {
reject(request.status);
}
}
};
request.open(method, url);
request.send(data);
});
}
var log = document.getElementById('test-promise-ajax-result');
var p = ajax('GET', '/api/categories');
p.then(function (text) { // 如果AJAX成功,获得响应内容
log.innerText = text;
}).catch(function (status) { // 如果AJAX失败,获得响应代码
log.innerText = 'ERROR: ' + status;
});
除了串行执行若干异步任务外,Promise还可以并行执行异步任务。
试想一个页面聊天系统,我们需要从两个不同的URL分别获得用户的个人信息和好友列表,这两个任务是可以并行执行的,用Promise.all()实现如下:
var p1 = new Promise(function (resolve, reject) {
setTimeout(resolve, 500, 'P1');
});
var p2 = new Promise(function (resolve, reject) {
setTimeout(resolve, 600, 'P2');
});
// 同时执行p1和p2,并在它们都完成后执行then:
Promise.all([p1, p2]).then(function (results) {
console.log(results); // 获得一个Array: ['P1', 'P2']
});
有些时候,多个异步任务是为了容错。比如,同时向两个URL读取用户的个人信息,只需要获得先返回的结果即可。这种情况下,用Promise.race()实现:
var p1 = new Promise(function (resolve, reject) {
setTimeout(resolve, 500, 'P1');
});
var p2 = new Promise(function (resolve, reject) {
setTimeout(resolve, 600, 'P2');
});
Promise.race([p1, p2]).then(function (result) {
console.log(result); // 'P1'
});
由于p1执行较快,Promise的then()将获得结果'P1'。p2仍在继续执行,但执行结果将被丢弃。
如果我们组合使用Promise,就可以把很多异步任务以并行和串行的方式组合起来执行。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
# JavaScript
# promise
# 如何从零开始利用js手写一个Promise库详解
# JS中promise化微信小程序api
# 浅谈js promise看这篇足够了
# JS 中使用Promise 实现红绿灯实例代码(demo)
# Javascript中Promise的四种常用方法总结
# 大白话讲解JavaScript的Promise
# JavaScript中Promise的使用详解
# Javascript中的神器——Promise
# 浅谈JavaScript中promise的使用
# 浅析Javascript ES6中的原生Promise
# Javascript Promise用法详解
# 都是
# 将来
# 还可以
# 可以用
# 并在
# 这两个
# 只需要
# 链式
# 个人信息
# 回调
# 如何处理
# 测试一下
# 是一个
# 是在
# 随机数
# 多个
# 会在
# 可以看到
# 将被
# 为例
相关文章:
网站制作的步骤包括,正确网址格式怎么写?
高防服务器租用首荐平台,企业级优惠套餐快速部署
如何通过建站之星自助学习解决操作问题?
非常酷的网站设计制作软件,酷培ai教育官方网站?
建站org新手必看:2024最新搭建流程与模板选择技巧
广平建站公司哪家专业可靠?如何选择?
如何在服务器上配置二级域名建站?
杭州银行网站设计制作流程,杭州银行怎么开通认证方式?
如何通过商城自助建站源码实现零基础高效建站?
齐河建站公司:营销型网站建设与SEO优化双核驱动策略
如何选择可靠的免备案建站服务器?
网站建设制作、微信公众号,公明人民医院怎么在网上预约?
建站之星下载版如何获取与安装?
如何通过FTP服务器快速搭建网站?
道歉网站制作流程,世纪佳缘致歉小吴事件,相亲网站身份信息伪造该如何稽查?
如何获取上海专业网站定制建站电话?
建站主机无法访问?如何排查域名与服务器问题
制作网站的公司有哪些,做一个公司网站要多少钱?
如何用搬瓦工VPS快速搭建个人网站?
如何快速搭建FTP站点实现文件共享?
北京的网站制作公司有哪些,哪个视频网站最好?
如何基于云服务器快速搭建网站及云盘系统?
高端建站如何打造兼具美学与转化的品牌官网?
关于BootStrap modal 在IOS9中不能弹出的解决方法(IOS 9 bootstrap modal ios 9 noticework)
网站制作多少钱一个,建一个论坛网站大约需要多少钱?
建站VPS能否同时实现高效与安全翻墙?
公司网站制作价格怎么算,公司办个官网需要多少钱?
jQuery 常见小例汇总
如何选购建站域名与空间?自助平台全解析
建站主机选购指南与交易推荐:核心配置解析
如何在建站之星网店版论坛获取技术支持?
制作网站的模板软件,网站怎么建设?
css网站制作参考文献有哪些,易聊怎么注册?
如何用AWS免费套餐快速搭建高效网站?
中山网站推广排名,中山信息港登录入口?
网站制作难吗安全吗,做一个网站需要多久时间?
家族网站制作贴纸教程视频,用豆子做粘帖画怎么制作?
郑州企业网站制作公司,郑州招聘网站有哪些?
建站之星后台管理如何实现高效配置?
北京网站制作的公司有哪些,北京白云观官方网站?
专业网站制作企业网站,如何制作一个企业网站,建设网站的基本步骤有哪些?
如何在VPS电脑上快速搭建网站?
义乌企业网站制作公司,请问义乌比较好的批发小商品的网站是什么?
网站制作网站,深圳做网站哪家比较好?
,网页ppt怎么弄成自己的ppt?
如何用花生壳三步快速搭建专属网站?
如何快速生成橙子建站落地页链接?
建站之星与建站宝盒如何选择最佳方案?
建站之星安装失败:服务器环境不兼容?
婚礼视频制作网站,学习*后期制作的网站有哪些?
*请认真填写需求信息,我们会在24小时内与您取得联系。