JavaScript异步队列进行try catch时的问题解决

目录
  • 一、前言
  • 二、主要讲的异步队列方法
    • 2.1 setTimeout
      • 2.1.1 问题表现
      • 2.1.2 问题原因
    • 2.2 Promise
    • 2.3 callback
    • 2.4 Async await

一、前言

我们在写js的时候,经常的会遇到需要异步去请求接口,或者通过setTimeout或Promise去做什么事, 然后让同步进程继续向下走, 当到某个时间节点的时候或者数据请求成功的时候在通过eventloop的方式回调执行。这本身是js的特点和优势。

但是,异步队列执行也存在错误的情况,这时,对于怎么进行错误处理,就成了我们的重点。

想一下项目中用到的方式,或者jquery的ajax方式,一般都会有catch、fail之类的回调方法供我们对错误结果进行处理。 那么现在讨论的话题是能不能使用try catch进行处理。

为什么写这篇文章? 是因为我在写JavaScript 的setTimeout与事件循环机制event-loop的时候,举例express的异步错误获取的时候,想到了这个点,我觉得有必要单独拿出来,写一篇断篇幅的,又能够清晰明了表达的一篇文章。于是这篇文章便生成了。

好了, 正文开始。

二、主要讲的异步队列方法

2.1 setTimeout

这里的setTimeout指的是一类,包括 setTimeoutsetInterval这类所谓宏任务。 他们可以用try catch来捕获错误么?

2.1.1 问题表现

    try{
        setTimeout(() => {
            let a = c;
        }, 100)
    } catch(e) {
        console.log('能获取到错误么??', e);
    }

结果是不能获取到,程序直接报错了, 那么出现的后果可能就是整个页面挂了,或者在node中,整个服务挂了。 我们的初心是想让程序更加健壮,但却做了无用功。

那么我们在想,既然在setTimeout 外边无法获取,那么能不能在setTimeout里面先用try catch获取一下,然后捕获到错误后再传出去呢? 想到就干,继续分析:

    try{
        setTimeout(() => {
            try {
                let a = c;
            } catch(e) {
                throw new Error('some variable is not defined');
            }
        }, 100)
    } catch(e) {
        console.log('能获取到错误么??', e);
    }

很抱歉,想法很好,但是也不行。外边也catch不到

2.1.2 问题原因

好了,我们把这个疑问分析一下吧。其实,这里的根本原因还是刚开始提到的事件循环。 事件循环不是空空的一句表述、一个概念,而是在代码中实实在在存在的。

具体事件循环的相关知识,可以看下我很早前写的JavaScript 的setTimeout与事件循环机制event-loop文章。

回到这个例子中, 最外层的try catch是在一个task中,我们定义它为我们js文件的同步主任务,从上到下执行到这里了, 然后,会把里面的setTimeout推到一个任务队列中, 这个队列是存储在内存中的,由V8来管理。然后主task就继续向下执行, 一直到结束。

当该setTimeout时间到了,且没有其它的task执行了, 那么,就将这个setTimeout的代码推入执行栈开始执行。 当执行到错误代码的时候,也就是这个 let a = c, 因为c未定义,所以就会报错。

但问题的本质是,这个错误跟最外层的try catch并不在一个执行栈中,当里面执行的时候,外边的这个task早已执行完, 他们的context(上下文)已经完全不同了。

所以,页面会直接报错,甚至程序崩溃。

2.2 Promise

我们知道,Promise 也是一个异步的处理过程,它对应事件循环中的微任务。 那么这里其实与上面的setTimeout存在同样的问题。

举个例子:

    try {
        new Promise((resolve, reject) => {
            reject('promise error');
        })
    } catch(e) {
        console.log('异步错误,能catch到么??', e);
    }

相信大家能够推导出结果了: 也catch不到

原因其实与上面的setTimeout是一样的,执行栈上下文已经不同了。

那么针对Promise,ECMA官方已经给我们提供了一个方法,那就是 catch, 通过catch我们获取到错误,可以阻止程序崩溃。 但是喜欢发散思维的你可能会想到, 那我用catch接到了,是不是就可以让外层的catch获取到了呢? 想到就试一下

    try {
        new Promise((resolve, reject) => {
            reject('promise error');
        }).catch(e => {
            throw new Error(e);
        })
    } catch(e) {
        console.log('异步错误,能catch到么??', e);
    }

结果就是 不行。相信大家通过我详细的例子和思维脉络,对这块已经真正掌握了吧?

2.3 callback

那么通过上面的,大家可能会有一种想法,只要是callback,就是catch不住的。 其实这种想法是错误的,我通过一个例子来证明。

    function Fn(cb) {
        console.log('callback执行了');
        cb();
    }

    try {
        const cb = () => {
            throw new Error('callback执行错误');
        }
        Fn(cb);
    } catch(e) {
        console.log('能够catch住么???')
    }

其实这里就是个烟雾弹, 考验大家对这个事件循环相关机制是不是明白了。

2.4 Async await

现在的项目中,大家越来越愿意使用Async await 这对 es7标准里的api了。 因为它们这对组合是在是太好用了。 那么通过异步等待的方式,用try catch能够行么?

那么咱们使用一个例子验证一下:

const asyncFn = () => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            reject('asyncFn执行时出现错误了')
        }, 100);
    })
}
const executedFn = async () => {
    try{
        await asyncFn();
    }catch(e) {
        console.log('拦截到错误..', e);
    }
}

如果执行一下,就发现: catch到了!

asyncFn里面是有 Promise的,为什么外边就能catch到了呢? 是不是跟上面讲的矛盾了呢? 其实并没有。 看我分析一下:

async-await 是使用生成器、promise 和协程实现的,wait操作符还存储返回事件循环之前的执行上下文,以便允许promise操作继续进行。当内部通知解决等待的承诺时,它会在继续之前恢复执行上下文。 所以说,能够回到最外层的上下文, 那就可以用try catch 啦。

到此这篇关于JavaScript异步队列进行try catch时的问题解决的文章就介绍到这了,更多相关JS try catch内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • JS异步函数队列功能实例分析

    本文实例讲述了JS异步函数队列功能.分享给大家供大家参考,具体如下: 场景: 做直播,会有入场消息,入场特效,用户如果有坐骑,需要给他展示几秒钟的坐骑特效,如果几个人同时进场,那该怎么展示呢?这时候就会想到setTimeout函数,对,思路不错,但是,异步函数队列怎么实现呢?直接上代码: var Queue = function() { this.list = []; }; Queue.prototype = { constructor: Queue, queue: function(fn) {

  • JavaScript错误处理try..catch...finally+涵盖throw+TypeError+RangeError

    目录 1.用途 2.语法 3.实操 1.用途 通常,如果发生错误,脚本就会立即停止,并在控制台将错误打印出来. 有了这个语句就可以捕获错误并执行合理操作,可以让程序继续执行下去 2.语法 try { // 代码... } catch (err) { //err是有关错误详细信息的对象 // 错误捕获,上面代码报错就会转到这个代码块,而不会停止运行 } finally { //无论是否有异常抛出或捕获它总是执行 } 这种语句可以嵌套 3.实操 Catch 捕获所有 error. 如果我们不知道如何

  • JavaScript 异步方法队列链实现代码分析

    在<javascript设计模式>中对这种方法作了比较详细的描述,实现方法的链式调用,只须让在原型中定义的方法都返回调用这些方法的实例对象的引用即可,看看书中的这段代码: 复制代码 代码如下: (function() { function _$(els) { this.elements = []; for (var i = 0, len = els.length; i < len; ++i) { var element = els[i]; if (typeof element == 's

  • JS异步宏队列微队列原理详解

    先看一张我绘制的原理图 原理图 setImmediate 也是宏任务,在 Node 环境下,微任务还有 process.nextTick JS 中用来存储待执行回调函数的队列包含 2 个不同特定的列队 宏列队:用来保存待执行的宏任务(回调),比如:定时器回调.DOM 事件回调.ajax 回调微 列队:用来保存待执行的微任务(回调),比如:promise的回调.MutationObserver 的回调 JS 执行时会区别这 2 个队列 JS 引擎首先必须先执行所有的初始化同步任务代码 每次准备取出

  • 聊聊Javascript中try catch的2个作用

    程序是从上到下顺序执行的,同时可以通过一些控制语句来改变执行的路线,受控制语句影响下,程序最终的执行路线就是控制流. js 里面的控制语句有 if.for.while.try catch 等,它们都会改变程序的走向. 程序是操作数据的,随着程序的运行,也就是控制流的前进而改变的数据叫做数据流. 很明显,数据流是依赖控制流的,程序分析里面的数据流分析也是要先做控制流分析. 比如这样一段代码: const a = 1; let b; if (a === 1) { b = '1111'; } else

  • JS异步宏队列与微队列原理区别详解

    1. 原理图 2. 说明 JS 中用来存储待执行回调函数的队列包含 2 个不同特定的列队 宏列队:用来保存待执行的宏任务(回调),比如:定时器回调.DOM 事件回调.ajax 回调 微列队:用来保存待执行的微任务(回调),比如:promise的回调.MutationObserver 的回调 JS 执行时会区别这 2 个队列 JS 引擎首先必须先执行所有的初始化同步任务代码 每次准备取出第一个宏任务执行前, 都要将所有的微任务一个一个取出来执行,也就是优先级比宏任务高,且与微任务所处的代码位置无关

  • 一分钟学会JavaScript中的try-catch

    导读: 在Java中我们使用try-catch进行异常处理,同样的JavaScript也提供了和异常处理类似的异常处理机制,本节我们将对JavaScript异常处理进行详细讲解. 1.1 如何进行错误处理 <script> var i = {}; //定义一个变量 i.func(); //调用一个不存在的方法 console.log("test"); //如果上一段代码出现错误,这段代码不会执行 </script> 查看控制台输出结果 从图1-1中能看出,当前程

  • 实例解析js中try、catch、finally的执行规则

    try:  语句测试代码块的错误,一般把可能会出错的代码放到这里 catch: 只有try里面的代码块发生错误时,才会执行这里的代码,参数err记录着try里面代码的错误信息 finally: 无论有无异常里面代码都会执行 try{ console.log(0); }catch (err){ console.log(1); console.log(hello); }finally { console.log(2); } //最后结果分别打印出 0 2 /* try{ a.b.c(); }catc

  • JavaScript队列函数和异步执行详解

    编辑注:在Review别人的JavaScript代码时曾看到过类似的队列函数,不太理解,原来这个是为了保证函数按顺序调用.读了这篇文章之后,发现还可以用在异步执行等. 假设你有几个函数fn1.fn2和fn3需要按顺序调用,最简单的方式当然是: fn1(); fn2(); fn3(); 但有时候这些函数是运行时一个个添加进来的,调用的时候并不知道都有些什么函数:这个时候可以预先定义一个数组,添加函数的时候把函数push 进去,需要的时候从数组中按顺序一个个取出来,依次调用: var stack =

  • 浅谈Vuejs中nextTick()异步更新队列源码解析

    vue官网关于此解释说明如下: vue2.0里面的深入响应式原理的异步更新队列 官网说明如下: 只要观察到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据改变.如果同一个 watcher 被多次触发,只会一次推入到队列中.这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作上非常重要.然后,在下一个的事件循环"tick"中,Vue 刷新队列并执行实际(已去重的)工作.Vue 在内部尝试对异步队列使用原生的 Promise.then 和 MutationOb

随机推荐