在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 () {});

Run

支持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()函数只关心自身的逻辑,并不关心具体的resolvereject将如何处理结果。

有了执行函数,我们就可以用一个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);
}

Run

Log:

可见Promise最大的好处是在异步执行的流程中,把执行代码和处理结果的代码清晰地分离了:

Promise还可以做更多的事情,比如,有若干个异步任务,需要先做任务1,如果成功后再做任务2,任何任务失败则不再继续并执行错误处理函数。

要串行执行这样的异步任务,不用Promise需要写一层一层的嵌套代码。有了Promise,我们只需要简单地写:

job1.then(job2).then(job3).catch(handleError);

其中,job1job2job3都是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);
}

Run

Log:

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);
});
}

Run

Result:

除了串行执行若干异步任务外,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,就可以把很多异步任务以并行和串行的方式组合起来执行。

读后有收获可以支付宝请作者喝咖啡,读后有疑问请加微信群讨论:

Promise https://www.liaoxuefeng.com/wiki/1022910821149312/1023024413276544的更多相关文章

  1. git 基本操作 https://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000/0013744142037508cf42e51debf49668810645e02887691000

    1.创建版本库 (即仓库  repository)简单理解为一个目录,这个目录里的所有文件都可以被git管理起来,每个文件的修改删除,git都能跟踪,一边任何时刻都可以追踪历史,或者在将来某个时刻可以 ...

  2. https://en.wikipedia.org/wiki/Green_threads

    https://en.wikipedia.org/wiki/Green_threads

  3. bc https://en.wikipedia.org/wiki/Gossip_protocol

    分布式容错性:分布式网络极其鲁棒,能够容忍部分节点的异常状态: 不可篡改性:一致提交后的数据会一直存在,不可被销毁或修改: 隐私保护性:密码学保证了数据隐私,即便数据泄露,也无法解析. 随之带来的业务 ...

  4. abi-api, arm target triplet https://en.wikipedia.org/wiki/ARM_architecture

    GNU软件用target triplet来描述工作平台,target triplet是一种规范化的名称,形如cpu-vendor-os(where os can be ‘system’ or ‘ker ...

  5. kerberos (https://en.wikipedia.org/wiki/Kerberos_(protocol))

    Protocol[edit] Description[edit] The client authenticates itself to the Authentication Server (AS) w ...

  6. 一点对Promise的理解与总结

    全手打原创,转载请标明出处:https://www.cnblogs.com/dreamsqin/p/10959411.html,多谢,=.=~ axios用多了就开始疑惑它里面到底是个啥,虽然总被告知 ...

  7. 记录面试龙腾简合-java开发工程师经历

    /** * ############ * 变强是会掉光头发的!现在的头发还是很茂盛,是该开心还是难过呢.. * ############ * / 总结下近期面试龙腾简合-java开发岗的经历.附上笔试 ...

  8. Promise与async/await与Generator

    Promise是什么: Promise是异步微任务(process.nextTick.Promise.then() catch() finally()等),用于解决异步多层嵌套回调的问题(回调地狱-- ...

  9. JavaScript之Promise对象

    含义 Promise 是异步编程的一种解决方案,比传统的解决方案--回调函数和事件--更合理和更强大.它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了 Promise 对象. ...

随机推荐

  1. 微信小程序——简易动画案例

    wxml: <view class="container"> <view animation="{{animation}}" class=&q ...

  2. PHP+Ajax点击加载更多内容 -这个效果好,速度快,只能点击更多加载,不能滚动自动加载

    这个效果好,速度快,只能点击更多加载,不能滚动自动加载 一.HTML部分 <div id="more"> <div class="single_item ...

  3. DB-SQLServer:SQLServer

    ylbtech--DB-SQLServer:SQLServer SQL是英文Structured Query Language的缩写,意思为结构化查询语言.SQL语言的主要功能就是同各种数据库建立联系 ...

  4. Java中移动文件或目录的方法盘点

    本文不再更新,可能存在内容过时的情况,实时更新请移步原文地址:Java中移动文件或目录的方法盘点: import org.apache.commons.io.FileUtils; import jav ...

  5. spotbus gradle-qulity-plugiin 多项目bug检查

    https://spotbugs.readthedocs.io/en/latest/bugDescriptions.html https://xvik.github.io/gradle-quality ...

  6. Mathtype部分数学符号不能显示,只能显示方框时的解决办法

    解决方法:打开C:\WINDOWS\Fonts,若里面有MT Extra(TrueType)字体或其快捷方式,则将其删除,再把MathType安装目录下\MathType 6.0\Fonts\True ...

  7. MAC中怎么安装python

    转自:https://blog.csdn.net/hou_manager/article/details/79555809 一.Python 介绍 Python介绍 Python3在2008年12月3 ...

  8. [转]js设计模式—发布订阅模式

    发布—订阅模式又叫观察者模式,它定义对象间的一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知.在javascript开发中,一般用事件模型来替代传统的发布—订阅模式.本文将 ...

  9. 读书笔记--Head First Python 目录

    1.初识python 2.共享你的代码 3.文件与异常 4.持久共享 5.推导数据 6.定制数据对象 7.web开发 8.移动应用开发 9.管理你的数据 10.扩展你的web应用 11.处理复杂性 其 ...

  10. OpenLayers使用symbolizers样式特征

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head ...