30 秒速记
- 承诺保存一次异步结果,落定后不能反转
- 执行器同步运行,反应回调异步安排
then返回新承诺,承接值、异常或其他承诺- 返回内层任务才能把它接进链
- 聚合失败不等于取消其他底层任务
Promise 像一张结果收据,先拿到收据,等工作结束后再读取成功值或失败原因。 执行器创建时同步执行,then 回调则异步运行;每次链式调用产生新承诺,回调返回值、抛错或返回的异步任务决定下一步结果。已经落定的状态不能翻转,但用另一个承诺解析时可能仍在等待它。写链条最容易漏掉 return,让内层任务脱离整体错误处理。
版本校准: 原理文章中的代码代表特定实现与写作时间。应用到当前项目时,应先确认浏览器、框架或工具的主版本,再区分稳定的规范语义、可变化的内部实现和项目自身约束。
# 一、Promise基础用法
# 1.1 基本用法
new Promise(function(resolve, reject) {
//待处理的异步逻辑
//处理结束后,调用resolve或reject方法
})
- 新建一个
promise很简单,只需要new一个promise对象即可。其中Promise是构造函数,创建出来的承诺是对象,它接受一个函数作为参数,并且会返回promise对象,这就给链式调用提供了基础- 其实
Promise函数的使命,就是构建出它的实例,并且负责帮我们管理这些实例。而这些实例有以下三种状态:
pending: 初始状态,未兑现或拒绝fulfilled: 意味着操作成功完成rejected: 意味着操作失败
pending状态的Promise对象可能以fulfilled状态返回了一个值,也可能被某种理由(异常信息)拒绝(reject)了。当其中任一种情况出现时,Promise对象的then方法绑定的处理方法(handlers)就会被调用,then方法分别指定了resolve方法和reject方法的回调函数

var promise = new Promise(function(resolve, reject) {
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});
promise.then(function(value) {
// 如果调用了resolve方法,执行此函数
}, function(value) {
// 如果调用了reject方法,执行此函数
});
上述代码很清晰的展示了
promise对象运行的机制。下面再看一个示例:
function getJSON(url) {
return new Promise((resolve, reject) => {
const client = new XMLHttpRequest()
client.open('GET', url)
client.responseType = 'json'
client.timeout = 10000
client.setRequestHeader('Accept', 'application/json')
client.onload = () => {
if (client.status >= 200 && client.status < 300) resolve(client.response)
else reject(new Error('HTTP ' + client.status))
}
client.onerror = () => reject(new Error('Network error'))
client.ontimeout = () => reject(new Error('Request timed out'))
client.onabort = () => reject(new Error('Request aborted'))
client.send()
})
}
getJSON("/posts.json").then(function(json) {
console.log('Contents: ' + json);
}, function(error) {
console.error('出错了', error);
});
上面代码中,
resolve方法和reject方法调用时,都带有参数。它们的参数会被传递给回调函数。reject方法的参数通常是Error对象的实例,而resolve方法的参数除了正常的值以外,还可能是另一个Promise实例,比如像下面这样。
var p1 = new Promise(function(resolve, reject){
// ... some code
});
var p2 = new Promise(function(resolve, reject){
// ... some code
resolve(p1);
})
上面代码中,
p1和p2都是Promise的实例,但是p2的resolve方法将p1作为参数,这时p1的状态就会传递给p2。如果调用的时候,p1的状态是pending,那么p2的回调函数就会等待p1的状态改变;如果p1的状态已经是fulfilled或者rejected,那么p2会跟随它的结果,已注册的反应回调仍要异步调度,不会在当前同步调用栈中立刻执行
# 1.2 promise捕获错误
Promise.prototype.catch方法是Promise.prototype.then(null, rejection)的别名,用于指定发生错误时的回调函数
getJSON("/visa.json").then(function(result) {
// some code
}).catch(function(error) {
// 处理前一个回调函数运行时发生的错误
console.log('出错啦!', error);
});
Promise对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被下一个catch语句捕获
getJSON("/visa.json").then(function(json) {
return json.name;
}).then(function(name) {
// proceed
}).catch(function(error) {
//处理前面任一个then函数抛出的错误
});
# 1.3 常用的promise方法
Promise.all方法
Promise.all方法用于将多个Promise实例,包装成一个新的Promise实例
var p = Promise.all([p1,p2,p3]);
Promise.all接受可迭代输入,成员可以是普通值、承诺或可同化对象,会按Promise.resolve的语义处理。结果保持输入顺序,空输入也有明确的成功结果。
p的状态由p1、p2、p3决定,分成两种情况
- 只有
p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数 - 只要
p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数
// 生成一个Promise对象的数组
var promises = [2, 3, 5, 7, 11, 13].map(function(id){
return getJSON("/get/addr" + id + ".json");
});
Promise.all(promises).then(function(posts) {
// ...
}).catch(function(reason){
// ...
});
Promise.race方法
Promise.race方法同样是将多个Promise实例,包装成一个新的Promise实例。
var p = Promise.race([p1,p2,p3]);
上面代码中,只要
p1、p2、p3之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的Promise实例的返回值,就传递给p的返回值
- 如果
Promise.all方法和Promise.race方法的参数,不是Promise实例,就会先调用下面讲到的Promise.resolve方法,将参数转为Promise实例,再进一步处理
Promise.resolve
有时需要将现有对象转为
Promise对象,Promise.resolve方法就起到这个作用
var jsPromise = Promise.resolve($.ajax('/whatever.json'));
上面代码将
jQuery生成deferred对象,转为一个新的ES6的Promise对象
- 如果
Promise.resolve方法的参数,不是具有then方法的对象(又称thenable对象),则返回一个新的Promise对象,且它的状态为fulfilled。
