Promise介绍
Promise是异步编程的一种解决方案,从语法上来看Promise是一个对象,从它可以获取异步操作的消息,简单来说,它就像是一个容器,里边保存着某个未来结束的事件,通常是获取异步操作的结果。Promise有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败),一旦状态改变就不会在改变,任何时候得到的都是这个结果。Promise状态改变无非两种结果,一种是从pending到fulfilled,一种是从pending到rejected
finally
finally方法用于指定不管Promise对象最后的状态如何,在执行完then或catch指定的回调函数以后,都会执行finally方法指定的回调函数
promise.then(value => {
状态为fulfilled 执行
}).catch(error => {
状态为rejected 执行
}) .finally(() => {
不管状态为什么都会执行
});
finally不接收任何参数,这就意味着没办法知道Promise返回的状态如何,那么,finally方法里边的操作与状态无关,不依赖Promise的执行结果
all
Promise.all()方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。
//三个状态都成功
const promise1 = new Promise((resolve,reject)=>{
resolve('success1')
})
const promise2 = new Promise((resolve,reject)=>{
resolve('success2')
})
const promise3 = new Promise((resolve,reject)=>{
resolve('success3')
})
const p = Promise.all([promise1,promise2,promise3])
p.then(data=>{
console.log(data); //三个都成功则成功
}, error=>{
console.log(error) // 只要有失败,则失败
})
这是上边代码执行出来的结果,现在让我看看有一个失败的会怎样
//三个状态都成功
const promise1 = new Promise((resolve,reject)=>{
resolve('success1')
})
const promise2 = new Promise((resolve,reject)=>{
reject('error')
})
const promise3 = new Promise((resolve,reject)=>{
reject('error1')
})
const p = Promise.all([promise1,promise2,promise3])
p.then(data=>{
console.log(data); //三个都成功则成功
}, error=>{
console.log(error) // 只要有失败,则失败
})
通过上面的代码可以看出,p的状态是由promise1 promise2 promise3决定的,分为两种情况:
- 只有promise1 promise2
promise3的状态都为fulfilled的时候,那么p的状态也为fulfilled,此时promise1 promise2
promise3的返回值组成一个数组,传给p的回调函数 - 只要 promise1 promise2 promise3 之中有一个被 rejected,p的状态就变成
rejected,此时第一个被reject 的实例的返回值,会传递给p的回调函数。
race
Promise.race()方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。
const promise1 = new Promise((resolve,reject)=>{
setTimeout(() => {
resolve('成功');
}, 2000);
})
const promise2 = new Promise((resolve,reject)=>{
setTimeout(() => {
reject('失败');
}, 5000);
})
const p = Promise.race([promise1,promise2])
p.then(data=>{
console.log(data);
}, error=>{
console.log(error)
})
从上边代码的结果可以看出,只要promise1 promise2 之中有一个实例率先改变状态,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给p的回调函数。
resolve
有时需要将现有对象转为 Promise 对象,Promise.resolve方法就起到这个作用。
resolve方法的参数分为四种情况:
- 参数是一个Promise实例的
resolve方法不做任何的处理,原封不动返回
- 参数是一个具有then方法的对象
let thenable = {
then: function(resolve, reject) {
resolve(42);
}
};
let p1 = Promise.resolve(thenable);
p1.then(function (value) {
console.log(‘H:’,value); // H:42
});
resolve方法会将这个对象转为Promise对象,然后立即执行对象里边的then方法
- 参数不是具有then()方法的对象,或根本就不是对象
const p = Promise.resolve('Hello');
p.then(function (s) {
console.log(s)
});
// Hello
如果参数是一个原始值,或者是一个不具有then方法的对象,则resolve方法返回一个新的 Promise 对象,状态为resolved。
- 不带参数的
const p = Promise.resolve();
p.then(function () {
// ...
});
resolve方法允许调用时不带参数,直接返回一个resolved状态的 Promise 对象。
所以,如果希望得到一个 Promise 对象,比较方便的方法就是直接调用resolve方法。
reject
reject方法也会返回一个新的 Promise 实例,该实例的状态为rejected。
const p = Promise.reject('出错了');
// 等同于
const p = new Promise((resolve, reject) => reject('出错了'))
p.then(null, function (s) {
console.log('H:',s)
});
// H: 出错了
上面代码生成一个 Promise 对象的实例p,状态为rejected,回调函数会立即执行。
reject方法的参数,会原封不动地作为reject的理由,变成后续方法的参数。
async await
async/await是JavaScript为了更好的解决异步问题而提出的一种解决方案,许多人将其称为异步的终极解决方案,下面让我们一起来看一下async/await
function timeout(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
async function asyncPrint(value, ms) {
await timeout(ms);
console.log(value);
}
asyncPrint('hello world', 2000);
使用注意点
- await命令后面的Promise对象,运行结果可能是rejected,所以最好把await命令放在try…catch代码块中。
async function myFunction() {
try {
await somethingThatReturnsAPromise();
} catch (err) {
console.log(err);
}
}
- 多个await命令后面的异步操作,如果不存在继发关系,最好让它们同时触发。
let foo = await getFoo();
let bar = await getBar();
上面代码中,getFoo和getBar是两个独立的异步操作(即互不依赖),被写成继发关系。这样比较耗时,因为只有getFoo完成以后,才会执行getBar,完全可以让它们同时触发。
可以写成这样:
let [foo, bar] = await Promise.all([getFoo(), getBar()]);
- await命令只能用在async函数之中,如果用在普通函数,就会报错
async function dbFuc(db) {
let docs = [{}, {}, {}];
// 报错
docs.forEach(function (doc) {
await db.post(doc);
});
}
优雅的错误处理方法
上边我们也说到async返回一个Promise对象,运行结果可能是rejected,所以最好把await命令放在try…catch代码块中,如果是有多个异步操作,需要对每个异步返回的error错误进行不同的处理,我们可以这样写:
const fetchDataA = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('fetch data is A')
}, 1000)
})
}
const fetchDataB = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('fetch data is B')
}, 1000)
})
}
const fetchDataC = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('fetch data is C')
}, 1000)
})
}
(async () => {
try {
const dataA = await fetchDataA()
console.log('dataA is ->', dataA)
} catch(err) {
console.log('err is ->', err)
}
try {
const dataB = await fetchDataB()
console.log('dataB is ->', dataB)
} catch(err) {
console.log('err is ->', err)
}
try {
const dataC = await fetchDataC()
console.log('dataC is ->', dataC)
} catch(err) {
console.log('err is ->', err)
}
})()
这样处理也不是不可以,但是代码里乍一看全是try…catch,看着不是那么优雅,这时我们是不是也想只用一个try…catch
// ... 这里 fetch 函数省略
(async () => {
try {
const dataA = await fetchDataA()
console.log('dataA is ->', dataA)
const dataB = await fetchDataB()
console.log('dataB is ->', dataB)
const dataC = await fetchDataC()
console.log('dataC is ->', dataC)
} catch(err) {
console.log('err is ->', err)
// 难道要定义 err 类型,然后判断吗??
/**
* if (err.type === 'dataA') {
* console.log('dataA err is', err)
* }
* ......
* */
}
})()
如果是这样的话那我们岂不是要写一对if else的判断,这时候我们在想async返回的是Promise对象,那么我们就可以用then方法
(async () => {
const fetchData = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('fetch data is me')
}, 1000)
})
}
const data = await fetchData().then(data => data ).catch(err => err)
console.log(data)
})()
在上面写法中,如果 fetchData 返回 resolve 正确结果时,data 是我们要的结果,如果是 reject 了,发生错误了,那么 data 是错误结果,这样我们就又要判断了,显然不太合适。
(async () => {
const fetchData = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('fetch data is me')
}, 1000)
})
}
const [err, data] = await fetchData().then(data => [null, data] ).catch(err => [err, null])
console.log('err', err)
console.log('data', data)
// err null
// data fetch data is me
})()
这样是不是好很多了呢,但是问题又来了,不能每个 await 都写这么长,写着也不方便也不优雅,并且会有很多的重复代码
(async () => {
const fetchData = () => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('fetch data is me')
}, 1000)
})
}
// 抽离成公共方法
const awaitWrap = (promise) => {
return promise
.then(data => [null, data])
.catch(err => [err, null])
}
const [err, data] = await awaitWrap(fetchData())
console.log('err', err)
console.log('data', data)
// err null
// data fetch data is me
})()
将对 await 处理的方法抽离成公共的方法,在使用 await 调用awaitWrap 这样看起来是不是就好多了
结语
上面就是我关于Promise和async/await理解的记录,
同时我也希望大家通过观看这篇文章能够有所收获!谢谢您的观看!
参考文献:
https://es6.ruanyifeng.com/
https://juejin.cn/post/6844903767129718791