2.5.13
https://jsfiddle.net/zr7rz6xh/8/
Vue.config.errorHandler = function (err, vm, info) {
alert("handler");
};
new Vue({
el: '#app',
mounted: function() {
return new Promise(function(resolve, reject) {
throw new Error('err');
});
}
})
errorHandler should be called
Uncaught (in promise) Error: err
Vue.config.errorHandler is not called if the error occurs inside a promise or await/async function.
I want to use the created hook with an await call, so it has to be async. But then the errorHandler is ignored.
I like that idea, since async
/await
is getting more common that would probably be a nice thing to have.
This feature would be a great add! Here's another example of how it currently works:
new Vue({
el: '#app',
methods: {
async asyncMethod() {
throw new Error('This does NOT get caught by Vue') // wish this were caught
},
regularMethod() {
throw new Error('This gets caught by Vue') // yay!
},
},
})
As a workaround, you can catch the error in a try / catch block. But I couldn't find a way to trigger an error on the Vue instance. Is this possible?
methods: {
async asyncMethod() {
try {
throw new Error('This does NOT get caught by Vue')
} catch (err) {
// Can you trigger the Vue instance's 'errorCaptured' callback from here?
}
}
},
I thought maybe the convention of emitting an error
event might work. I think that or something similar could be helpful.
this.$emit('error', error) // no special treatment of 'error' event
I think it is very comfortable for Vuex actions. I wish to be implemented asap.
vuex/actions.js
async POST_SOMETHING({commit}, params) {
await requireAuthAction()
const { data } = await axios.post(`/api/something`, params)
commit("POST_SOMETHING", data)
},
main.js
Vue.config.errorHandler = (err) => {
alert(err.message)
}
Until this issue is fixed I am using a hacky mixin to catch errors thrown in async methods and send them to errorHandler:
import Vue from 'vue'
// This mixin fixes following issue:
// errorHandler does not work with async component methods
// https://github.com/vuejs/vue/issues/7653
export default {
beforeCreate() {
const methods = this.$options.methods || {}
Object.entries(methods).forEach(([key, method]) => {
if (method._asyncWrapped) return
const wrappedMethod = function (...args) {
const result = method.apply(this, args)
const resultIsPromise = result && typeof result.then == 'function'
if (!resultIsPromise) return result
return new Promise(async (resolve, reject) => {
try {
resolve(await result)
} catch (error) {
if (!error._handled) {
const errorHandler = Vue.config.errorHandler || console.error
errorHandler(error)
error._handled = true
}
reject(error)
}
})
}
wrappedMethod._asyncWrapped = true
methods[key] = wrappedMethod
})
},
}
@Doeke Any way to get that working with the errorCaptured
hook? (from a glance it doesn't seem like it would)
@ErikBjare you could loop through this
component and $parent
s after or before errorHandler(error)
to check for existance of vm.$options.errorCaptured
hooks. Personally I went back to catching exceptions manually again in components, and using unhandledrejection/rejectionhandled
to send actual unhandled rejections to analytics (which only works on Chrome)
Hi, I expanded Doeke's mixin to simulate propagation to errorCaptured hooks, which may also be async. Not sure it works 100% to spec, but it may help someone temporarily until this issue is resolved:
import Vue from 'vue'
// This mixin fixes following issue:
// errorHandler does not work with async component methods
// https://github.com/vuejs/vue/issues/7653
async function propagateErrorCaptured(component, error, vm) {
let continuePropagation = true
const ec = component.$options.errorCaptured
if (ec instanceof Array) {
for (let i = 0; i < ec.length; i++) {
continuePropagation = ec[i].apply(component, [error, vm])
if (typeof continuePropagation === "object" && typeof continuePropagation.then === "function") {
// wait for the promise
continuePropagation = await continuePropagation
}
if (!continuePropagation) break;
}
}
if (component.$parent && continuePropagation) {
return await propagateErrorCaptured(component.$parent, error, vm)
} else {
return continuePropagation
}
}
export default {
beforeCreate() {
const methods = this.$options.methods || {}
Object.entries(methods).forEach(([key, method]) => {
const wrappedMethod = function (...args) {
const result = method.apply(this, args)
const resultIsPromise = result && typeof result.then == 'function'
if (!resultIsPromise) return result
return new Promise(async (resolve, reject) => {
try {
resolve(await result)
} catch (error) {
const continuePropagation = await propagateErrorCaptured(this, error, this)
if (!continuePropagation) {
if (Vue.config.errorHandler) {
Vue.config.errorHandler(error, this)
} else {
reject(error)
}
}
}
})
}
methods[key] = wrappedMethod
})
},
}
@Doeke Hi, your code is nice, but if it catch error by reject('......') ?
Closed via #8395 (will be out 2.6)
Is there a way for me to install [email protected] before it's released? I tried specifying https://github.com/vuejs/vue.git#2.6 in my dependencies, but then I get an error about
vue-template-compiler must be installed as a peer dependency, or a compatible compiler implementation must be passed via options.
I have vue-template-compiler in my devDependencies (^2.5.17) but for some reason it's not finding it, even though Vue 2.5.17 did.
If not, I'll just wait until 2.6 is out, but this is something we just recently found we needed. Thanks.
@daviesdoclc we just released 2.6.0-beta.1
@yyx990803 thanks. I just tried it but it's not solving my issue. It does solves the initial issue addressed at the beginning of this thread. However this still spits out "Uncaught (in promise) Error" in my case where I'm calling a plain javascript service for example (simplified below).
function service() {
return new Promise((resolve, reject) => {
reject(new Error("err"))
})
}
Vue.config.errorHandler = function(err, vm, info) {
console.log("errorHandler", err)
}
new Vue({
el: "#app",
mounted: function() {
// ** THIS WORKS ***
// return new Promise((resolve, reject) => {
// reject(new Error("err"))
// })
// ** THIS DOESN'T ***
service()
}
})
Suggestions on how to handle this pattern?
(if I shouldn't be commenting on a closed issue let me know)
@daviesdoclc return service()
?
@yyx990803 sorry, that was a bad example. Yes, putting return on that does work in that scenario. However, that isn't my real world scenario, I was just trying to simplify. Here's where I'm having trouble.
created() {
AppJsonService.getTopForGenre(this.genre).then((_ranks) => {
this.ranks = _ranks
})
// it would be nice not to have to do this and have it handled by the Vue.config.errorHandler
// .catch((error) => {
// console.log(error)
// })
this._scrollCount = 0
}
During created I am calling several services to get and set data elements. In this particular case it would be nice to not have to catch everything, but instead have it call the general error handler. This was the case I hoped it would work with.
I found this blog entry https://blog.bugsnag.com/anatomy-of-a-javascript-error/ that describes this situation. The suggestion is to either use a catch statement for all promise chains OR use unhandledrejection (which is only supported on Chrome right now). Sorry to have confused this with The Vue issue mention in this ticket.
Unless I'm missing something, https://github.com/f2009/vue/commit/c6c6d795bbb21453fb1aa027c76ce7c79edd4c6d doesn't seem to account for async watcher callbacks. JSFiddle
I'm also facing some issues with async methods.
My project uses Typescript through https://github.com/kaorun343/vue-property-decorator and even with Vue 2.6 when doing: throw new Error('aaa')
it's not working in an public async mounted()
whereas return Promise.reject('aaa')
will trigger the errorCaptured
of my main component.
Most helpful comment
I like that idea, since
async
/await
is getting more common that would probably be a nice thing to have.