Promise对象就是一个容器,里面保存着未来才会结束的事件的结果。
- 有pending(进行中)、fulfilled(已成功)、reject(已失败)三种状态。
- 状态是不可逆的,一旦进行完成,状态就固定了。只有两种可能:从pending变成fulfilled,或者从pendfing变成reject.记录的是这个Promise对象是否进行了。
用法
定义一个Promise对象,对象中异步操作成功时(例如文件读取成功,网络请求成功等),触发promise对象状态从pending变成resolve,并将异步操作的结果,作为参数传递给resolved状态的回调函数。相反,异步操作失败时,触发reject,对象状态从pending变成reject,并将异步操作报出的错误,作为参数传递给reject的回调函数。
const promise = new Promise(function(resolve, reject) {
// ... some code
if (/* 异步操作成功,即promise对象态度从pending变成resolve*/){
resolve(value);
} else {
reject(error);
}
});
两个状态的回调函数可以在then方法中指定:
promise.then(function(value) {
// success
}, function(error) {
// failure
});
两个回调函数时作为参数传递给then方法的,第一个参数是异步事件resolved回调函数,第二个是reject的回调函数。第二个为可选。两个函数都可接受对象传出的值作为参数,即异步事件的结果或者错误信息。
promise新建后会立即执行,但对象中的异步事件需要等待同步任务执行完才会执行。
Promise对象中定义的两个函数参数,除了是正常值之外,也可能是另一个promise实例。当参数是promise实例时,会由参数实例的状态值决定回调函数何时执行。只有当作为参数的promise发生了状态改变,即异步事件执行了,对象状态从pending变成resolved或者reject后,回调函数才会执行。
promise对象中参数函数的执行不受回调函数调用的影响。可以理解为对象本身时同步任务,所有里面的执行不受异步任务的影响。
new Promise((resolve, reject) => {
resolve(1);
console.log(2);
}).then(r => {
console.log(r);
});
// 2
// 1
上面代码中,调用resolve(1)以后,后面的console.log(2)还是会执行,并且会首先打印出来。这是因为立即 resolved 的 Promise 是在本轮事件循环的末尾执行,总是晚于本轮循环的同步任务。
所以一般会将异步任务的后续操作放在then方法里。为避免promise参数函数里的同步操作扰乱执行顺序。可在resolve前加上return,这样后面语句就不会再执行。
Promise.prototype.then()
then方法为了让promise实例所共享,时定义在原型对象上的。then方法中定义对象实例状态改变时的回调函数。**then方法返回的是一个新的Promise实例。**记录的是对象的状态变化与否。变化了便会触发then方法中中对应的回调函数。
采用链式的then,可以指定一组按照次序调用的回调函数。这时,前一个回调函数,有可能返回的还是一个Promise对象(即有异步操作),这时后一个回调函数,就会等待该Promise对象的状态发生变化,才会被调用。
Promise.prototype.catch()
Promise.prototype.catch()方法是.then(null, rejection)或.then(undefined, rejection)的别名,用于指定发生错误时的回调函数。
但是也有些许差别。then中定义的reject方法不会被张一个实例对象运行中排除错误触发,而catch方法可以捕获。
即异步函数执行失败,状态值变为reject和then()方法中回调函数运行过程中抛出错误时,会触发reject()方法,传递给下一个then()方法的状态是reject,都会被catch()方法捕获。
p.then((val) => console.log('fulfilled:', val))
.catch((err) => console.log('rejected', err));
// 等同于
p.then((val) => console.log('fulfilled:', val))
.then(null, (err) => console.log("rejected:", err));
promise对象参数函数中抛出错误会触发reject()方法。但如果Promise状态已经变成resolved,再抛出错误是无效的。根本原因是因为Promise对象只会有一种存在状态,一旦resolved,即表示异步操作成功了,就算继续抛出错误,也不会改变对象的状态。所以传到下一个then方法的参数状态值其实是resolved,不会被catch捕获。
const promise = new Promise(function(resolve, reject) {
resolve('ok');
throw new Error('test');
});
promise
.then(function(value) { console.log(value) })
.catch(function(error) { console.log(error) });
// ok
正是由于异步事件本身的reject和回调函数中错误都会传递reject给后面的天很(),被catch语句捕获。这种链式执行顺序让Promise对象的错误拥有“冒泡”性质,会一直往后传递,直到被捕获为止。而且链式中只要有一个promise实例发生错误状态变成reject,后面的操作就不会再执行。所以可以将catch方法放在执行链的最后用于捕获所有实例的错误信息。