定义
ES2015正式发布(也就是ES6),其中Promise被列为正式规范。作为ES6中最重要的特性之一,我们有必要掌握并理解透彻。
Promise 是一个对象,它用来标识 JavaScript 中异步操作的状态(pending, resolve, reject)及结果(data)。
可以通过控制台打印出来一个Promise 对象来看下 console.dir(Promise)
它是一个构造函数,既有属于自己私有的 resolve, reject, all, race等方法,也有protype 原型上的 then, catch等方法。
Promise的状态
三种状态:
pending(进行中,初始状态)
fulfilled(成功状态,也叫resolved)
rejected(失败状态)
Promise只有两个过程(状态):
(1)pending -> fulfilled : Resolved(已完成)
(1)pending -> rejected:Rejected(已拒绝)
通过函数resolve将状态转为fulfilled,函数reject将状态转为rejected,状态一经改变就不可以再次变化。
一个 promise 对象只能改变一次, 无论变为成功还是失败, 都会有一个结果数据 ,成功的结果数据一般称为 value, 失败的结果数据一般称为 reason。
为什么要用Promise
特点:
(1)将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。流程更加清晰,代码更加优雅。
(2)Promise对象提供统一的接口,使得控制异步操作更加容易。
缺点:
(1)无法取消Promise,一旦新建它就会立即执行,无法中途取消。
(2)如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。
(3)当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成
Promise的用法
- 模拟问题:3秒后请求完成,返回数据res.data
var p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('res.data');
}, 3000);
});
p.then((val) => {
console.log(val);
// 'res.data'
});
console.log(p);
// [object Promise]
a. Promise.resolve() 的用法
Promise.resolve()方法可以将现有对象转为Promise 对象。
var p = Promise.resolve($.ajax('/something.data'));
p.then((val) => {console.log(val)});
它等价于
var p = new Promise(resolve => {
resolve($.ajax('/something.data'))
});
p.then((val) => {console.log(val)});
b. Promise.reject() 用法
此方法和Promise.resolve()方法类似,除了rejecet 代表状态为 Rejected,不多说。
c. Promise.all() 用法
用于将多个Promise 实例包装成一个新的 Promise实例,参数为一组 Promise 实例组成的数组。
var p = Promise.all([p1,p2,p3]);
当 p1, p2, p3 状态都 Resolved 的时候,p 的状态才会 Resolved;只要有一个实例 Rejected ,此时第一个被 Rejected 的实例的返回值就会传递给 P 的回调函数。
应用场景:假设有一个接口,需要其它两个接口的数据作为参数,此时就要等那两个接口完成后才能执行请求。
var p1 = new Promise((resolve, reject) => {
setTimeout(resolve, 1000, 'P1');
});
var p2 = new Promise((resolve, reject) => {
setTimeout(resolve, 2000, 'P2');
});
// 同时执行p1和p2,并在它们都完成后执行then:
Promise.all([p1, p2]).then((results) => {
console.log(results); // 获得一个Array: ['P1', 'P2']
});
d. Promise.race() 用法
和Promise.all 类似,区别是 Promise.race() 只要监听到其中某一个实例改变状态,它的状态就跟着改变,并将那个改变状态实例的返回值传递给回调函数。
应用场景: 可以通过多个异步任务来进行容错处理,多个接口返回同样的数据,只要有一个接口生效返回数据即可。
e. Promise.prototype.then()
then 方法是定义在 Promise 的原型对象上的,作用是为 Promise 实例添加状态改变时的回调函数;
then() 返回一个新的Promise 实例,因此可以支持链式写法。
链式写法的一个例子//来自廖雪峰
// 0.5秒后返回input*input的计算结果:
function multiply(input) {
return new Promise((resolve, reject) => {
console.log('calculating ' + input + ' x ' + input + '...');
setTimeout(resolve, 500, input * input);
});
}
// 0.5秒后返回input+input的计算结果:
function add(input) {
return new Promise((resolve, reject) => {
console.log('calculating ' + input + ' + ' + input + '...');
setTimeout(resolve, 500, input + input);
});
}
var p = new Promise((resolve, reject) => {
console.log('start new Promise...');
resolve(123);
});
p.then(multiply)
.then(add)
.then(multiply)
.then(add)
.then(function (result) {
console.log('Got value: ' + result);
});
f. Promise.prototype.catch()
catch 方法是一个语法糖,看下面代码就明白了,用于指定发生错误时的回调函数。
var p = new Promise((resolve, rejecet) => {
if (...) {resolve()};
else {reject()};
})
p.then((val) => {
console.log('resolve:', val);
}).catch((err) => console.log('reject:', err));
// 等同于
p.then((data) => {
console.log(data);
}, (err) => {
console.log(err);
})
// 后一种写法更好,语义更清晰,第一种方法在第一个函数里面出错的话时在第二个函数里监听不到变化的。
g. Promise.try()
实际开发中,经常遇到一种情况:不知道或者不想区分,函数 f 是同步函数还是异步操作,但是想用 Promise 来处理它。因为这样就可以不管f是否包含异步操作,都用 then 方法指定下一步流程,用 catch 方法处理 f 抛出的错误。
- 第一种方法,缺陷是不能识别同步请求。
const f = () => console.log('now');
Promise.resolve().then(f);
console.log('next');
// next
// now
- new Promise() 写法
const f = () => console.log('now');
(
() => new Promise(
resolve => resolve(f())
)
)();
console.log('next');
// now
// next
- Promise.try 写法,替代new Promise() 方法,更简洁。
const f = () => console.log('now');
Promise.try(f);
console.log('next');
// now
// next
async await 和Promise
1.执行async函数,返回的都是Promise对象
async function test1() {
return 1;
};
async function test2() {
return Promise.resolve(1);
};
const res1 = test1();
const res2 = test2();
console.log('res1', res1);
console.log('res2', res2);
2.Promise.then 成功的情况,对应的是await
async function test3() {
const p3 = Promise.resolve(3);
p3.then(data => {
console.log('data',data);
});
const data = await p3;
console.log(data);
}
test3();
//data 3
//3
3.Promise.catch 异常的情况, 对于try…catch…
async function test3() {
const p3 = Promise.reject(3);
p3.catch(data => {
console.log('data',data);
});
try {
const data = await p3;
console.log('data',data);
} catch (err) {
console.log('err', err);
}
}
test3();
//data 3
// error 3
4. 如何让Promise顺序执行?应用场景是什么?
const p1 = new Promise(resolve => {
setTimeout(()=> {
resolve(1);
}, 3000);
});
const p2 = new Promise(resolve => {
setTimeout(()=> {
resolve(2);
}, 2000);
});
const p3 = new Promise(resolve => {
setTimeout(()=> {
resolve(3);
}, 1000);
});
async function execute() {
await p1.then(data => console.log(data));
await p2.then(data => console.log(data));
await p3.then(data => console.log(data));
};
execute();
//1 //2 //3