#Promise async/await总结
1. JS中的异步操作哪些?
- 定时器
settimeout
for (var i = 0; i <10; i++) {
setTimeout(()=>{
console.log(i);
}, 0);
}
//=>10 //=>10 //=>10 //=>10 //=>10 //=>10 //=>10 //=>10 //=>10 //=>10
问:下面打印的结果是?
console.log('1')
setTimeout(()=>{console.log('2')},0)
console.log('3')
------------------------
// 1
// 3
// 2
- 事件绑定
addEventListener
,onclick
for (var i = 0; i < tabList.length; i++) {
//tabList[i] <=>每一轮循环当前要操作的LI DOM对象
tabList[i].onclick = function () {
alert(i);
changeTab(i);//=>需要把当前点击的这个LI的索引传递进来
}
}*/
当绑定完毕事件后,进行点击发现i全都是3了
-
AJAX
一般采取异步处理 promise
async/await
2. 处理异步的方式有哪些?
2.1 回调函数
回调callback
是一个函数被作为一个参数传递到另一个函数里,在那个函数执行完后再执行。( B函数被作为参数传递到A函数里,在A函数执行完后再执行B )
假定有两个函数f1和f2,f2等待f1的执行结果,f1()-->f2();如果f1很耗时,可以改写f1,把f2写成f1的回调函数:
function f1(callback){
setTimeout(function () {
callback(); // f1的任务代码
}, 1000);
}
f1(f2); // 执行
采用回调的方式,把同步操作变成了异步操作,f1不会堵塞程序运行,相当于先执行程序的主要逻辑,将耗时的操作推迟执行。
回调函数是异步编程最基本的方法,其优点是简单、容易理解和部署,缺点是不利于代码的阅读和维护,各个部分之间高度耦合,流程会很混乱,而且每个任务只能指定一个回调函数。
注意 区分 回调函数和异步,回调是实现异步的一种手段,并不一定就是异步。
回调也可以是同步,如:
//下面没有任何有关异步的函数操作,所以是同步的
function A(callback){
console.log("I am A");
callback(); //调用该函数
}
function B(){
console.log("I am B");
}
A(B);
2.2 事件监听
采用事件驱动模式,任务的执行不取决代码的顺序,而取决于某一个事件是否发生。
监听函数有:on
,bind
,listen
,addEventListener
,observe
举例,为f1绑定一个事件(jquery写法):f1.on('done',f2);即当f1发生done事件,就执行f2。
function f1(){
settimeout(function(){
// f1的任务代码
f1.trigger('done'); // 执行完成后,立即触发done事件,从而开始执行f2
},1000);
}
优点:易理解,可绑定多个事件,每一个事件可指定多个回调函数,可以去耦合,有利于实现模块化
缺点:整个程序都要变成事件驱动型,运行流程会变得不清晰
2.3 发布订阅
2.4 Promise
2.4.1 Promise基本概述
- promise的三种状态
pending
,fulfilled
,rejected
,状态只能是3个中的一种 - 状态改变结果就定死了,不能再改变状态
-
resolve
,reject
其实就是你在声明Promise中的一道往外传递参数的门,.then()
中的参数就是你从这个门往外resolve
的参数,.cathch()
中的参数就是你从这个门往外reject
的参数
2.4.2 基本用法
new Promise
时候会立刻执行
控制台能反映当前promise的状态,因为我没有resolve或reject,状态就不会发生改变
new Promise((resolve,reject)=>{console.log('1')})
//=> 1
里面加入resolve我们看看他的状态
resolve.png
可以看到Promise的状态变成了resolve
return new Promise
return new Promise
一般情况下我们把Promise
封装到一个函数中,在我们需要处理异步的时候进行调用,就需要进行return操作,当我们调用函数的时候实际上是进行上面的new Promise
立即执行,new promise还是同步处理的
const ajaxPromise= param => {
return new Promise((resovle, reject) => {
$.ajax({
...
"success": res => {
resovle(res);
},
"error": err => {
reject(err);
}
})
})
}
promise中return的问题
一般情况下我们只考虑一种参数状态处理的时候就不需要return,比如上面的代码块,如果进入success
时,success的回调函数就只有一局resolve,而且这是最后一句话,执行完就resolve出去了,就不用进行return操作,但是如果回调的时候考虑返回值的多种状态就需要进行return,否则resolve后面的函数仍会执行
function AA(a){
return new Promise((resolve,reject)=>{
if(a==3){
console.log('resolve前面的语句')
resolve('resolve的结果')
console.log('resolve后面的语句')
}
else{
reject
}
})
}
AA(3).then(res=>{console.log(res)})
//=>resolve前面的语句
//=>resolve后面的语句
//=>resolve的结果
return在起到中断返回的作用,但是是promise中then()的参数只能接受由resolve出去的,return的参数是无效的
function AA(a){
return new Promise((resolve,reject)=>{
if(a==3){
return '3'
resolve('resolve的结果')
}
else{
reject
}
})
}
AA(3).then(res=>{console.log(res)})
//=> 无反应
then的使用
then方法返回的是一个新的Promise实例(注意,不是原来那个Promise实例)。因此可以采用链式写法,即then方法后面再调用另一个then方法。
function AA(a){
return new Promise((resolve,reject)=>{
if(a==3){
resolve('resolve的结果')
}
})
}
AA(3).then(res1=>{return AA(3)})
.then(res2=>{console.log(res2)})
//=> resolve的结果
只要在then中return出去的都会被包装为新的Promise实例,无论你return出去的是什么东西,并且return的值不可能return到promise外面被其他变量接收(解决办法:1.callback
2.async swait
)
function AA(a){
return new Promise((resolve,reject)=>{
if(a==3){
resolve('resolve的结果')
}
})
}
AA(3).then(res=>{return '3'})
.then(res=>{console.log(res)})
//=> 3
后续链式的.then只能接受resolve到的东西,而不会一个new Promise函数
function AA(a){
return new Promise((resolve,reject)=>{
if(a==3){
resolve('resolve的结果')
}
})
}
AA(3)
.then(res=>{return AA(4)})
.then(res=>{console.log(res)})
.catch(res=>{console.log('catch ' + res)})
//=>catch 4
catch的使用
catch
用于捕获reject
从而达到rejected
状态
.catch实际上是.then里面的第二个参数
function AA(a){
return new Promise((resolve,reject)=>{
if(a==3){
resolve('resolve的结果')
}
})
}
AA(3)
.then(res=>{return Promise.reject('在catch捕获')})
.then(res=>{console.log('then' + res)},err=>{console.log('第二个参数位置' + err)})
//=>第二个参数位置在catch捕获
若不放回调里面,cathc
无法捕获是哪个位置发生的异常
function AA(a){
return new Promise((resolve,reject)=>{
if(a==3){
resolve('resolve的结果')
}
reject(4)
})
}
AA(3)
.then(res=>{return AA(4)})
.then(res=>{console.log(res)})
.catch(res=>{console.log(res)})
.catch(res=>{console.log(res)})
如果我们想要第二个catch捕获,不用async写的话只能写在回调函数里面,这是promise的缺点(轻微回调地狱)
function AA(a){
return new Promise((resolve,reject)=>{
if(a==3){
resolve('resolve的结果')
}
reject(4)
})
}
AA(3)
.then(res=>{
AA(4)
.then(res=>{
console.log(res)
})
.catch(res=>{
console.log(res)
})
})
.catch(res=>{console.log(res)})
如果涉及多种catch判别,回调现象就很严重了
distance(this.hazardInfo.hazardLatitude, this.hazardInfo.hazardLongitude)//定位打卡
.then(res => {
uploadMedia(this)//上传媒体
.then(res => {
// console.log(res);
this.$http.post('URL', this.hazardInfo) //上传表单
.then(res => {
console.log(res);
uni.showToast({
icon: 'none',
title: '上传成功',
success() {
setTimeout(() => {
uni.navigateBack({
delta: 1
});
}, 2000);
}
});
})
.catch(err => {
console.log(err);
uni.showToast({ icon: 'none', title: '表单上传失败' });
});
})
.catch(() => {
uni.showToast({ icon: 'none', title: '媒体上传失败' });
});
})
.catch(type => {
switch (type) {
case 0:
uni.hideLoading();
uni.showToast({
icon: 'none',
title: '当前未在距离范围,请调整你的位置!',
position: 'bottom'
});
break;
case 1:
uni.hideLoading();
uni.showToast({
icon: 'none',
title: '定位失败,请开始定位权限',
position: 'bottom'
});
default:
break;
}
});
}
如果写成链式调用,你会发现永远是第一个catch捕获,你可能会说,我就在一个catch里面进行res的类型判断不就行了,那么我问你,如果涉及到多个AJAX的promise错误,500的错误都是一样的,你怎么进行类别的判断。如果是你自己封装的promise到可以,如果是用axios这种第三方库是肯定不行的。
3. async/await
由上面可以总结Promise
有两个缺点
- 涉及多种catch类型捕获,需要写到回调函数里面,如果链式写法会永远被第一个catch捕获
- then里面的值无法return出去,只能被下一个then进行操作
3.1 async
async函数永远返回的是promise对象,对,这种机制很像promise中then返回出去的东西,他只能是一个promise对象,无论你返回的是什么
async function testAsy(){
return 'hello world';
}
let result = testAsy();
console.log(result)
promise状态.png
那么意味着可以对result进行
then
操作
async function testAsy(){
return 'hello world';
}
let result = testAsy();
result.then(res=>{console.log(res)})
//=> hello world
catch
操作
async function testAsy(){
return Promise.reject('error')
}
let result = testAsy();
result
.then(res=>{console.log(res)})
.catch(res=>{console.log(res)})
//=>error
所以当没有await语句执行async函数,它就会立即执行,返回一个Promise对象,非阻塞,与普通的Promise对象函数一致
3.2 await
await如果等待的是Promise对象,则返回Promise的处理结果;如果是其他值,则返回该值本身await并不会进行等待。并且await会暂停当前async function的执行,等待Promise的处理完成。
await可以把primise返回的值给指定变量这是Promise语法无法实现的
如果await等的是Promise
function testAsy(x){
return new Promise(resolve=>{setTimeout(() => {
resolve(x);
}, 3000)
}
)
}
async function testAwt(){
let result = await testAsy('hello world');
console.log(result);
console.log('tangj')
}
testAwt();
console.log('tangSir')
//=>tangSir
//=>3秒钟之后出现hello world
//=>tangj
如果await等的是其他值
await等待是是其他的值,那么个同步任务没有区别
function testAsy(x){
return new Promise(resolve=>{setTimeout(() => {
resolve(x);
}, 3000)
}
)
}
async function testAwt(){
let result = await 'no Promise';
console.log(result);
let res = await testAsy('promise');
console.log(result); // 3秒钟之后出现hello world
console.log(res )
console.log('tangj') // 3秒钟之后出现tangj
}
testAwt();
console.log('tangSir') //立即输出tangSir
//=>tangSir
//=>no Promised
//=>3秒钟之后出现no Promise
//=>promise
//=>tangj
await缺点:是只能接受resolve的结果,无法处理catch的结果,但是会对reject进行报错,从而影响async函数的继续执行
function testAsy(x) {
return new Promise((resolve,reject) => {
if (x == 1) {
resolve('1')
return
}
reject(2)
})
}
async function testAwt(){
let res = await testAsy(2)
console.log(res )
console.log(3)
}
testAwt()
报错 不会打印下面的res和3.png
如果不想影响后面异步的进行(比如进入web首页,多页面迸发ajax,不能让其中一个错误的AJAX影响后面的AJAX请求),以下有两种操作
- try catch
- .catch
下面我只对.catch进行说明,try catch在我看来反而还不如promise美观
function testAsy(x) {
return new Promise((resolve,reject) => {
if (x == 1) {
resolve('1')
return
}
reject(2)
})
}
async function testAwt(){
let res = await testAsy(2).catch(err=>{console.log(err)})
console.log(res )
console.log(3)
}
testAwt()
//=> 2
//=> undefined
//=> 3
那么我上面的promise嵌套回调就可以进行下面的写法
async start(){
let location = await distance(this.hazardInfo.hazardLatitude, this.hazardInfo.hazardLongitude)
let meida = await uploadMedia()
let form = await this.$http.post('URL', this.hazardInfo)
}
记得把原来的各种catch处理放到封装的promise函数的reject之前就行了
如果我们想既要catch异常,又不想继续往下执行,有两种办法
- 解构赋值,适用于promise返回的为对象
let { data } = await this.$http.post('/api', { username: username, password: password }}).catch(err => {
this.isRotate = false;
});
console.log(data)
console.log('1')
//=>TypeError: undefined is not an object (evaluating '_ref.data') 无法解构导致报错,无法往下进行
- 用普通变量接promise返回值,适用于任何类型的返回值
let data = await this.$http.post('/api, { username: this.username, password: this.password }}).catch(err => {
this.isRotate = false;
});
console.log(data)
console.log('1')
if(data == undefined ) return
console.log('2')
//=>undefined
//=>1