这两天面试,遇到好几个人,都是那种我感觉我肚子里的墨水都吐出来完了,难不倒人家,于是问了下家里那位老狗,从最开始就念叨着你问他try-catch在有return的情况下怎么执行的,执行结果是啥,我前面没理,后面确实有点遭不住了,来看看吧,肚子里添点墨水,别把脸丢大了~
做了几个测试:
一、数据执行顺序以及返回结果
原始:try
-catch
-finally
方法正确书写
created() {
console.log('outer')
console.log(this.tryCatch())
},
methods: {
tryCatch() {
let a = 1
try {
console.log('===try inner')
a = 3
console.log('try 2')
} catch {
console.log('=====catch inner')
} finally {
console.log('======finally inner')
console.log('===finally 2')
console.log(a)
}
}
},
运行没毛病。
然后开始变种了,加上 return
会怎么样呢?:
// 1. 在 finally 当中加 return
finally {
console.log('======finally inner')
a = 4
console.log('===finally 2')
return a
}
其他不变,只是 finally
return
的结果被返回回去了。
再来在 try
中直接返回就返回 a
的值,而 finally
不改变呢?
try {
a = 3
return a
} ... finally {
console.log('======finally inner')
console.log('===finally 2')
}
有趣的是,try
中赋了值的 a
的值被返回了,同时 finally
的语句也执行了。
那么,同时在 try
finally
中都返回值,最后会获得什么呢?
try {
a = 3
return 3
} ... finally {
console.log('======finally inner')
a = 4
console.log('===finally 2')
reteurn 4
}
结果显而易见,以 finally
返回的值为准了。
这,我就有点兴奋了,要是有一个是异步呢?
不慌,先看看 catch
,保留 try
中的返回值,我在 try
中故意写错语句,让其进入 catch
中去,:
try {
a = 3
a.b = 'test'
return a
} catch {
console.log('=====catch inner')
console.log(a)
} finally {
console.log('======finally inner')
console.log(a)
a = 4
console.log('===finally 2')
return a
}
运行结果还是 4
,且 try
中错误之前的语句都执行了的:
那么我在 catch
当中赋值呢?
...catch{
console.log('=====catch inner')
console.log(a)
a = 10
} finally {
console.log('======finally inner')
console.log(a)
a = 4
console.log('===finally 2')
return a
}
仍然是4,但是传入给 finally
的 a
的值是 catch
中赋的值!如果在 catch
当中 return a
也是一样的,会进入到 finally
中去,那如果 finally
不返回值呢?
实验两种情况:
一种是 try
中无错,但 try
catch
中都有返回值:
try {
console.log('===try inner')
a = 3
return a
console.log('try 2')
} catch {
console.log('=====catch inner')
console.log(a)
a = 10
return a
} finally {
console.log('======finally inner')
console.log(a)
a = 4
console.log('===finally 2')
}
这种情况下,最终的返回值 a
为3;
第二种情况是 try
中既有返回值,也有错误
try {
console.log('===try inner')
a = 3
a.b = 'test'
return a
console.log('try 2')
} catch {
console.log('=====catch inner')
console.log(a)
a = 10
return a
} finally {
console.log('======finally inner')
console.log(a)
a = 4
console.log('===finally 2')
}
此种情况下最终返回值为 10
。
最后,不再在 finally
中加个错误代码都对不起我严谨的工作态度!
...finally{
console.log('======finally inner')
console.log(a)
a = 4
a.c = '1'
console.log('===finally 2')
}
结果,报错了…
由此可看出:try
-catch
-finally
是顺序执行的,catch
会在 try
出现错误的时候基于 try
中的结果继续运行,最终数据流入到 finally
中,而 finally
,如果没有返回值,那就找它之前的步骤的 返回值
返回,每一个 过程
都有一个独立的隔离域(命名空间),数值在其中的赋值等操作是生效的,但如果在那一步不返回,则最终返回值的计算结果以有返回值那个为准。
另外,finally
中如果出现错误,是不会再次进入 catch
中的,直接报错拉!
得了,搞半天,就是个流水线和命名空间的事儿。上一次的执行返回结果会进入到下一流程,如果没有返回结果,则对外来说流程作废,内部流程还是进行了的。
说到这,我还没开始验证异步赋值的情况,又对数据是个对象有点感兴趣了!
二、变量为对象的情况下的返回结果
基于上一步的结论分析,这次只是多定义一个对象,然后 finally
中不 return
,但是会改变对象值,看看最终的结果:
tryCatch() {
let a = 1
const obj = {
try: '',
catch: '',
finally: ''
}
try {
console.log('===try inner')
a = 3
obj.try = 'try'
console.log('obj: ', obj)
return { a, obj }
console.log('try 2')
} catch {
console.log('=====catch inner')
console.log('a: ', a)
console.log('obj: ', obj)
a = 10
obj.catch = 'catch'
return { a, obj }
} finally {
console.log('======finally inner')
console.log('a: ', a)
a = 4
console.log('a: ', a)
console.log('obj: ', obj)
obj.finally = 'finally'
console.log('obj: ', obj)
console.log('===finally 2')
}
}
结果也跟预想的一样,肯定是遵守语言的特性的。引用类型的对象的属性值数据会被后面的步骤更改(即使不返回)。
tryCatch() {
let a = 1
const obj = {
try: '',
catch: '',
finally: ''
}
try {
console.log('===try inner')
a = 3
obj.try = 'try'
a.b = 'test'
console.log('obj: ', obj)
return { a, obj }
console.log('try 2')
} catch {
console.log('=====catch inner')
console.log('a: ', a)
console.log('obj: ', obj)
a = 10
obj.catch = 'catch'
return { a, obj }
} finally {
console.log('======finally inner')
console.log('a: ', a)
a = 4
console.log('a: ', a)
console.log('obj: ', obj)
obj.finally = 'finally'
console.log('obj: ', obj)
console.log('===finally 2')
}
}
此时,对象的值是不影响的(都会被执行)。
最后剩下异步的问题了…
三、在 try
catch
finally
中有异步设置数据的情况下,怎么返回呢?
首先我们照样分别定义一个基本类型数值,和引用类型的,再来对比,代码比较多,直接上代码:
created() {
console.log('outer')
console.log(this.tryCatch())
},
methods: {
tryCatch() {
let a = 1
const obj = {
try: '',
catch: '',
finally: ''
}
let promiseValue = 0
const promiseObj = {
try: '',
catch: '',
finally: ''
}
try {
console.log('===try inner')
a = 3
obj.try = 'try'
console.log('obj: ', obj)
console.log('promiseValue before: ', promiseValue)
promiseValue = this.A(promiseValue)
console.log('promiseValue after: ', promiseValue)
return { a, obj, promiseValue, promiseObj }
console.log('try 2')
} catch {
console.log('=====catch inner')
console.log('a: ', a)
console.log('obj: ', obj)
a = 10
obj.catch = 'catch'
return { a, obj, promiseObj, promiseValue }
} finally {
console.log('======finally inner')
console.log('a: ', a)
a = 4
console.log('a: ', a)
console.log('obj: ', obj)
obj.finally = 'finally'
promiseValue = 123
new Promise((resolve) => {
console.log('===promise inner')
promiseObj.finally = 'finally'
promiseValue = 456
resolve()
}).then(() => {
console.log('promise then')
promiseObj.finally = 'finally - '
})
console.log('obj: ', obj)
console.log('promiseObj: ', promiseObj)
console.log('===finally 2')
}
},
async A(a) {
console.log('==== function A ')
await new Promise(resolve => setTimeout(resolve, 1000))
return this.B(a)
},
B(a) {
console.log('===== function B ')
a = 111
return a
}
},
运行结果:
由运行结果可以看出,在 try
catch
finally
中,只影响最终的返回值,其他的遵照语言特性(相当于函数嵌套,有返回值则重新赋值,无返回值则形成作用域隔离,对最终值无影响,只是运行,如果传入参数为引用类型,则值会被更改)。
四、补充一个前端 promise
中使用 try
catch
finally
的运行结果
由之前的结果联想到前端请求中的运行等是否相同,于是在项目中做了如下测试:
async created() {
console.log('====created')
console.log(await this.getTryCatch())
}
methods: {
getTryCatch() {
return new Promise((resolve, reject) => {
console.log('====主')
resolve('a')
}).then(res => {
console.log('=====then1:')
console.log(res)
return 'then1 success'
}).then(res => {
console.log('====then2:')
console.log(res)
return 'then2 success'
}).catch(err => {
console.log('===catch:')
console.log(err)
return 'catch error'
}).finally(() => {
console.log('====finally')
console.log('=======down===')
return 'finally success'
})
},
}
首先,在 Promise
中,必须用 resolve
reject
这个进入下一环节没毛病;
然后按照之前的案例来写 then
catch
finally
,then
我写了两个,用于检测返回值到底怎么返回。
首先,全部都有 return
的情况,运行结果如下:
可见,返回值为最后一个 then
的返回值。跟 try
catch
相同,所有的步骤都会执行。
第二步,我们去掉第二个 then
中的 return
,查看返回值:
结果为 undefined
,所以必须返回最后一个 then
的返回值。
最后一步,去除所有 then
中的返回值,结果如下:
then
中无返回值,但 finally
中有返回值,最后返回的结果为:undefined
catch
同理。
由上得出结论:
在 Promise
中,只会返回 then
或 catch
的返回值,finally
中的返回值无效,但会执行每个阶段的代码。
再看修改对象的执行(基本能猜到是一样的):
async created() {
console.log('====created')
const obj = {
then1: '',
then2: '',
catch: '',
finally: ''
}
console.log(await this.getTryCatch(obj))
console.log(obj)
}
methods: {
getTryCatch(obj) {
return new Promise((resolve, reject) => {
console.log('====主')
resolve('a')
}).then(res => {
console.log('=====then1:')
obj.then1 = 'then1'
console.log(res)
return 'then1 success'
}).then(res => {
console.log('====then2:')
console.log(res)
obj.then2 = 'then2'
// return 'then2 success'
}).catch(err => {
console.log('===catch:')
console.log(err)
obj.catch = 'catch'
return 'catch error'
}).finally(() => {
console.log('====finally')
console.log('=======down===')
obj.finally = 'finally'
obj.then1 = 'finally then1'
return 'finally success'
})
},
}
最后执行结果:
结果来了,跟 try
catch
一样!
下课!