async await promise try...catch
一、几者之间联系
简单介绍下这几个的关系
1
2
3
4
5
6
7
8
9
10
11
12
13
14async function buildData(name) {
try {
let response1 = await axios.get('/api/user?name=' + name);
let userInfo = response1.data;
let response2 = await axios.get('/api/topics?user_id' + userInfo._id);
let posts = response2.data;
// i got it.
} catch(err) {
console.log(err);
}
}
buildData('xiaoming');async
在函数声明前使用async关键词修饰 说明函数中有异步操作
await
等待 后面的代码执行完毕 再继续向下执行
promise
Promise 是一个对象,从它可以获取异步操作的消息,知道异步函数是完成了还是出错了。
axios返回的结果就是一个promisetry catch
try catch JavaScript的异常捕获机制,凡是在try语句块中的代码出错了,都会被catch捕获。
上面的代码就是说
- buildData 这个函数被 async 修饰 说函数中有异步操作
- await 等待异步操作结果
- 如果有错误发生 使用try catch 捕获异常
二、详解
(一)promise
从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。
三个状态
Promise字面上讲,是一个承诺。这个承诺有三个状态
- pending 进行中(悬而未决)
- fulfilled 已成功(以满足)
- rejected 已失败(已拒绝)
两个特点
- 对象的状态不受外界的影响。只有异步操作的结果可以决定当前是哪种状态。
- 状态一旦改变 就不会再变,任何时候都可以得到这个结果。promise对象状态的改变只有两种情况,
- pending 到 fulfilled
- pending 到 rejected
只要这两种情况发生,状态就凝固了,不会再改变了,会一直保持这个结果,这时就定型了 resolved。
如果改变已经发生了,任何时候添加回调函数,得到的都是这个结果。因为创建Promise对象时,回调函数中有resolve和reject两个参数,后续的resolved统一指的是fulfilled状态,不包括rejected状态
一个Promise对象一旦状态确定了,它的使命也就结束了。后面的代码都不应该再执行了,最好return resolve();
如果状态已经resolve了,再在后面抛出错误也是无效的,也不会改变状态为rejected,后面有异常也不会抛出Promise对象如何知道异步操作结果又如何传递(resolve、reject)
Promise对象如何知道异步操作的结果呢,那就是回调函数了,一个表示成功resolve,一个表示失败reject,
ES6 规定,Promise对象是一个构造函数,用来生成Promise实例。
Promise实例
1
2
3
4
5
6
7
8
9var promise = new Promise(function(resolve, reject) {
// ... some code
console.log(我一创建就执行了)
if (/* 异步操作成功 */){
resolve(value);
} else {
reject(error);
}
});Promise新建后,就会立即执行,返回一个Promise对象。
Promise构造函数需要一个函数作为参数,这个函数有两个参数,分别是resolve和reject,
它们是两个函数,由 JavaScript 引擎提供,不用自己部署resolve: 把Promise状态 从 pending 变为 resolved ,在异步操作成功时调用,并将异步操作的结果,作为参数传递出去
reject: 把Promise状态 从 pending 变为 reject ,在异步操作失败时调用,并把错误作为参数传递出去
此外,reject方法的作用,也等同于抛出异常。reject的参数会被catch捕获。即便没有调用reject,如果执行过程中出错了,也会被catch捕获。
所以:
- 异步操作成功时,把结果告诉resolve回调函数 异步操作失败 把错误告诉reject回调函数
- resolve 和 reject 回调函数 还有一个作用就是把 结果传递出去
Promise对象如何使用异步函数的执行结果(then、catch)
then(指定了resolve状态和reject的回调函数)
1
2
3
4
5promise.then(function(value) {
// success
}, function(error) {
// failure
});then方法的参数是两个回调函数,都接受Promise对象传出的值作为参数:
- 第一个参数是resolve状态的回调函数
- 第二个参数是reject的回调函数, 这个参数是可选的
通常这个参数也不写,因为Promise实例还有一个方法叫catch是专门用来捕获异常的。
catch(专门用来捕获Promise对象产生的错误)
catch 是 .then(null, rejection)的别名,用于指定发生错误时的回调函数。
一旦catch前面的任何一个Promise发生异常,都会被catch捕获,包括Promise函数创建的Promise,还有.then返回的Promise,甚至catch前面如果还有一个catch在这个catch抛出的异常也会被后一个catch捕获。
也就是说:
Promise对象的错误具有冒泡性质,会一直向后传递,直到被捕获为止,也即是说,错误总会被下一个catch语句捕获。所以,既然这个catch这么厉害,then函数中的第二个参数常常被省略了,然后被这个catch方法替代。
所以通常这么写:
promise.then().catch()
promise.then().then().catch()
promise.then().then().catch().then().catch()所以下面例子第二种写法好些。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16// bad
promise
.then(function(data) {
// success
}, function(err) {
// error
});
// good
promise
.then(function(data) { //cb
// success
})
.catch(function(err) {
// error
});一般总是建议,Promise 对象后面要跟catch方法,这样可以处理 Promise 内部发生的错误。catch方法返回的还是一个 Promise 对象,因此后面还可以接着调用then方法和catch方法。
(二)try catch
try catch是JavaScript的异常处理机制,把可能出错的代码放在try语句块中,如果出错了,就会被catch捕获来处理异常。如果不catch 一旦出错就会造成程序崩溃。
如果有多个await命令,可以将其都放在try catch结构中,如果执行出错,catch会去捕获异常
1
2
3
4
5
6
7
8
9
10
11
12async function f() {
try {
await Promise.reject('出错了');
console.log('上面已经出错了');
return await Promise.resolve('hello world');
} catch(e) {
console.log(e);
}
}
f()
.then(v => console.log(v))
catch会去捕获try代码块中的错误,只要有一个抛出了异常,就不会继续执行,所以上面的代码不会打印上面已经出错了也不会执行return await Promise.resolve(‘hello world’);
因为使用了trycatch 所以 async 是顺利执行完成的,其中的报错 被 try catch处理了,所以异常不会被async返回的Promise的catch捕获,因此async返回的Promise对象状态是resolved。
(三)Promise.all()
Promise.all可以将多个Promise实例包装成一个新的Promise实例。同时,成功和失败的返回值是不同的,成功的时候返回的是一个结果数组,而失败的时候则返回最先被reject失败状态的值。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21let p1 = new Promise((resolve, reject) => {
resolve('成功了')
})
let p2 = new Promise((resolve, reject) => {
resolve('success')
})
let p3 = Promse.reject('失败')
Promise.all([p1, p2]).then((result) => {
console.log(result) //['成功了', 'success']
}).catch((error) => {
console.log(error)
})
Promise.all([p1,p3,p2]).then((result) => {
console.log(result)
}).catch((error) => {
console.log(error) // 失败了,打出 '失败'
})Promse.all在处理多个异步处理时非常有用,比如说一个页面上需要等两个或多个ajax的数据回来以后才正常显示,在此之前只显示loading图标
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16let wake = (time) => {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(`${time / 1000}秒后醒来`)
}, time)
})
}
let p1 = wake(3000)
let p2 = wake(2000)
Promise.all([p1, p2]).then((result) => {
console.log(result) // [ '3秒后醒来', '2秒后醒来' ]
}).catch((error) => {
console.log(error)
})
需要特别注意的是,Promise.all获得的成功结果的数组里面的数据顺序和Promise.all接收到的数组顺序是一致的,即p1的结果在前,即便p1的结果获取的比p2要晚。这带来了一个绝大的好处:在前端开发请求数据的过程中,偶尔会遇到发送多个请求并根据请求顺序获取和使用数据的场景,使用Promise.all毫无疑问可以解决这个问题。
(四)Promise.race()
顾名思义,Promse.race就是赛跑的意思,意思就是说,Promise.race([p1, p2, p3])里面哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17let p1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve('success')
},1000)
})
let p2 = new Promise((resolve, reject) => {
setTimeout(() => {
reject('failed')
}, 500)
})
Promise.race([p1, p2]).then((result) => {
console.log(result)
}).catch((error) => {
console.log(error) // 打开的是 'failed'
})
原理是挺简单的,但是在实际运用中还没有想到什么的使用场景会使用到。
- 本文作者:wowangmouren
- 本文链接:https://wangwewntao.top/2023/11/08/wmr_19/index.html
- 版权声明:本博客所有文章均采用 BY-NC-SA 许可协议,转载请注明出处!