Promise
简介
Promise
的出现是方便开发人员优雅地书写处理JavaScript
中的异步任务。由于它是ECMAScript 6(ES6)
中新增的类所以对于浏览器版本有一定的要求,具体如下:
Chrome | Edge | Firefox | Safari | Opera |
---|---|---|---|---|
Chrome 58 | Edge 14 | Firefox 54 | Safari 10 | Opera 55 |
JavaScript
中关于异步处理
- 在
Promise
出现之前在JavaScript
中通常使用回调函数去处理异步任务结束后的工作。它的作用就好比我们告诉异步任务等你结束后要去干什么。
setTimeout(function () {
console.log('3秒后打印这条信息'); // 三秒后子线程执行
}, 3000);
console.log('我先打印'); // 主线程先执行
- 其中
setTimeout
的第一个参数就是一个回调函数,第二参数为等待时间。目前来看使用回调函数处理异步任务结束之后的工作并没有什么不妥,那为什么还要提出Promise
呢? - 回调函数缺点:上边的示例中的异步任务都是一次异步,如果出现多次异步又该如何处理呢,可能就需要使用下边传统多次回调方式,这种方式虽然实现了多次异步处理但是最后生成的代码难以维护并且对于异常的处理非常不友好,这种方式也就是常说的回调陷阱。当然也可以使用
Promise
进行改造以达到同样效果。
// 传统多次回调
setTimeout(function () {
console.log("First");
setTimeout(function () {
console.log("Second");
setTimeout(function () {
console.log("Third");
}, 3000);
}, 2000);
}, 1000);
// 使用Promise达到同样效果
new Promise(function (res, rej) {
setTimeout(function () {
console.log("First");
res();
}, 1000);
}).then(function () {
return new Promise(function (res, rej) {
setTimeout(function () {
console.log("Second");
res();
}, 2000);
});
}).then(function () {
setTimeout(function () {
console.log("Third");
}, 3000);
});
Promise
登场
-
构造函数:
Promise
的构造函数接收一个函数作为参数,这个函数是同步的并且会被立即执行,也称之为起始函数。起始函数包含两个参数res(resolve)
和rej(reject)
,分别表示Promise
的成功和失败状态。起始函数执行成功时,它应该调用res
函数并传递成功的结果。当起始函数执行失败时,它应该调用rej
函数并传递失败的原因。 -
Promise
构造函数返回一个Promise
对象,该对象具有以下几个方法:then
:用于处理Promise
成功状态的回调函数。catch
:用于处理Promise
失败状态的回调函数。finally
:无论Promise
是成功还是失败,都会执行的回调函数。
-
一个小栗子
// 创建一个Promise对象
const promise = new Promise((res, rej) => {
// 异步操作
setTimeout(() => {
if (Math.random() < 0.5) {
res('小于0.5');
} else {
rej('大于等于0.5');
}
}, 1000);
});
// 调用Promise对象的then和catch方法
promise.then(res => {
console.log(res); // 当生成随机数小于0.5时,打印构造函数中res传递出的'小于0.5'
}).catch(err => {
console.log(err); // 当生成随机数大于等于0.5时,打印构造函数中rej传递出的'大于等于0.5'
}).finally(() => {
console.log('总是会执行'); // 总是会打印此条信息
});
- 异步函数(
async、await
)几乎被所有浏览器支持。
使用async
修饰的函数会返回一个Promise
对象,可以使用then方法进行一些处理。而await
命令后边通常会跟一个Promise
对象,返回该对象的结果,如果不是Promise
则直接返回对应的值。 - 再来一个小栗子
// print是一个被async修饰的函数它返回的是一个Promise对象,正常return的值会被当做res函数的参数传递
async function print(num) {
if (num == 0) {
// 模拟函数内部报错,错误信息可以被catch捕获
throw new Error("Divide zero");
}
// 模拟耗时操作
let set = new Set();
for (let i=0; i<10000000; i++) {
set.add(i);
}
const result = 100 / num;
return result;
}
// await后跟一个Promise对象,并且等待Promise对象执行完毕才会继续向下执行。参数修改为0则会被捕获异常也会继续往下执行
await print(10)
// 正常结果
.then(res => {
console.log(res)
})
// 异常处理
.catch(err => {
console.log(err);
});
// 这条打印会被阻塞,只有等待之前的print函数执行完毕才会执行打印
console.log('Over! Over!');
- 多个
Promise
全部执行完毕后再进行一些操作可以借助Promise.all(params)
,all
函数接收一个Promise
对象数组,然后会将数组中全部的Promise
对象执行完毕后再进行相关处理。下面又是一个小栗子:
const p1 = new Promise(function(res, rej) {
setTimeout(function() {
console.log('First');
res('FirstResInfo');
}, 1000);
});
const p2 = new Promise(function(res, rej) {
setTimeout(function() {
console.log('Second');
res('SecondResInfo');
}, 2000);
});
const p3 = new Promise(function(res, rej) {
setTimeout(function() {
console.log('Third');
res('ThirdResInfo');
}, 3000);
});
const promises = [p1, p2, p3];
Promise.all(promises)
/*
* 当p1、p2、p3全部执行完毕后会执行打印,此时的res不再是一个单独的字符串,
* 而是一个字符串数组:['FirstResInfo', 'SecondResInfo', 'ThirdResInfo']
*/
.then(res => console.log(res))
.catch(err => console.log(err));
- 以上内容只是本人自己的一些关于
Promise
最基本用法的见解,肯定会存在不足之处。请大家友好讨论,不足之处希望大家多多包容。