zoukankan      html  css  js  c++  java
  • 关于 promise 吃到错误的理解

    关于 promise 吃到错误的理解

    下面的内容需要对浏览器原生支持的 promise 的基本用法有了解,如果你还不知道 promise 和 promise 的 catch 方法,你可能需要先在 这里 了解一下。

    在 阮一峰大神的 《ECMAScript 6 入门》 关于 Promise 对象那一章在介绍 Promise.prototype.catch() 方法时,里面有一句描述是这样写的 :

    跟传统的try/catch代码块不同的是,如果没有使用catch方法指定错误处理的回调函数,Promise 对象抛出的错误不会传递到外层代码,即不会有任何反应。

    一开始我 错误的理解 了这句话的意思,认为是在 promise 内部如果发生错误的话,可以使用 catch 方法来捕获错误,但是如果不使用 catch 方法,它会静悄悄的,不会有报错,就像加了 try-catch 但是在 catch 里面不进行任何处理。

    然后我做了一些测试发现和我想的不一样:

    首先,试一下在 promise 里面抛出一个异常,看看能不能通过 catch 捕获到

    var promise = new Promise(function(resolve, reject) {
        throw new Error('test');   // 这里抛了一个异常
    });
    promise
        .then(function(value) { console.log(value) })
        .catch((err) => console.log('promise catch err'))
    
    // 控制台输出:
    // promise catch err
    

    没什么问题,catch 方法成功捕获到异常了,但是这个异常会不会再被抛到外面去呢?为了以防万一,我们在window 对象上加个 onerror 事件监测一下看看

    window.onerror = function () {
        console.log('window err')
    } 
    
    var promise = new Promise(function(resolve, reject) {
        throw new Error('test');
    });
    promise
        .then(function(value) { console.log(value) })
        .catch((err) => console.log('promise catch err'))
    
    // 控制台输出:
    // promise catch err
    

    结果还是一样,说明 catch 方法成功捕获到异常而且没有继续往外面抛,这和书里说的一样

    那如果去掉 catch 方法呢?我们继续往下看

    var promise = new Promise(function(resolve, reject) {
        throw new Error('test');
    });
    promise.then(function(value) { console.log(value) })    // 这里不捕获异常了
    
    // 控制台输出:
    // Uncaught (in promise) Error: test
    

    这里控制台输出了一个报错:Uncaught (in promise) Error: test,咦,如果像书里说的:

    跟传统的try/catch代码块不同的是,如果没有使用catch方法指定错误处理的回调函数,Promise 对象抛出的错误不会传递到外层代码,即不会有任何反应。

    上面说不会有任何反应,那怎么控制台还会报错呢?

    我们可以猜测一下,其实这里有点像使用 try-catch 那样捕获了异常然后打印出错误信息后就不再做其他处理一样:

    try{
        console.log(x)
    }catch(err){
        console.error(err)
    }
    

    然后我们加一个 window.onerror 发现,这时 promise 外面添加错误监听事件不会捕获到 promise 对象里面没有进行捕获的错误,像下面这样:

    window.onerror = function () {   // 我们添加了 window 的 onerror 处理函数
        console.log('window err')
    } 
    
    promise = new Promise(function(resolve, reject) {
        throw new Error('test');
    });
    promise.then(function(value) { console.log(value) })   
    
    // 控制台输出:
    // Uncaught (in promise) Error: test   
    

    可以看到 window 的错误处理事件并没有被触发,所以报错应该是 promise 内部捕获处理的时候直接打印的而没有被抛出,也就验证了我们上面的猜测。

    所以到这里,我们可以把书上那句话重新翻译一下得到一条结论

    promise 对象里面同步代码抛出的错误在没有通过 promise 的 catch 方法捕获时是会打印报错的(不会阻止 promise 外面代码的执行),但是不会传递到外面触发其他错误监听函数(比如 window.onerror 、try-catch 等)


    书上还讲到一个东西也挺奇怪的

    window.onerror = function () {
        console.log('window err')
    } 
    
    var promise = new Promise(function (resolve, reject) {
        setTimeout(function () { 
            throw new Error('test') 
        }, 0)
    
        resolve('ok');
    });
    promise
        .then(function (value) { console.log(value) })
        .catch(() => console.log('promise catch err'))
    

    根据我们上面的理解,你觉得这个控制台会显示什么东西呢?

    如果你对答案是 promise catch err,那你就错了,他的结果是:

    // 控制台输出:
    // ok
    // window err
    // Uncaught Error: test
    

    这里由于是在 setTimeout 里面抛出错误的,所以报错会在同步代码执行完后的下一轮 “事件循环” 里执行,也就是说当 setTimeout 里面的函数执行后报错时,promise 已经执行完了(所以就算 resolve('ok') 写在 setTimeout 下面也是先输出 ok),所以这个错误是在 Promise 函数体外抛出的,当然也就不会被 promise 的 catch 方法捕获,所以就会传到 window 上被捕获并输出 window err,然后再被浏览器捕获输出Uncaught Error: test,如果在 window onerror 处理程序里面 return true,就不会看到浏览器捕获输出的 Uncaught Error: test 报错。

    再看一下 下面的两个例子:

    这里是在 promise 里面同步执行了 throw Error 和 resolve 两个操作

    window.onerror = function () {
        console.log('window err');
    } 
    
    var promise = new Promise(function (resolve, reject) {
        throw new Error('test');
        resolve('ok');
    });
    promise.then(function (value) { console.log(value) })
        .catch(() => console.log('promise catch err'))
    
    // 控制台输出:
    // promise catch err
    

    这里是在 promise 里面用 setTimeout 异步执行了 throw Error 和 resolve 两个操作

    window.onerror = function () {
        console.log('window err');
    } 
    
    var promise = new Promise(function (resolve, reject) {
        setTimeout(function () {               // 这里包了一个 setTimeout
            throw new Error('test');
            resolve('ok');
        }, 0)
    });
    promise.then(function (value) { console.log(value) })
        .catch(() => console.log('promise catch err'))
    
    // 控制台输出:
    //window err
    //Uncaught Error: test
    

    可以看到第二个例子中就算 promise 的状态还是 pedding ,异步操作里面的报错也不会被 promise 的 catch 方法捕获

    所以这里又可以得到一条结论

    Promise.prototype.catch() 方法对错误处理和捕获的规则只对 promise 里面的同步执行代码有效,如果此时 promise 里面有异步操作出错的话,是不受 promise 这些规则限制的,而是像正常的报错一样处理。

    总结:

    1. promise 对象里面同步代码抛出的错误在没有通过 promise 的 catch 方法捕获时是会打印报错的(不会阻止 promise 外面代码的执行),但是不会传递到外面触发其他错误监听函数(比如 window.onerror 、try-catch 等)
    2. Promise.prototype.catch() 方法对错误处理和捕获的规则只对 promise 里面的同步执行代码有效,如果此时 promise 里面有异步操作出错的话,是不受 promise 这些规则限制的,而是像正常的报错一样处理。
    3. 其实上面两条规则可以看出 promise 对错误的处理应该是在内部使用了像 try-catch 的方式处理了错误,所以异步的它是处理不了的。就行下面这样一样,try-catch 并不会捕获到错误,错误会被 window.onerror 捕获
    window.onerror = function () {
        console.log('window err');
        return true
    } 
    
    try{
        setTimeout(function () { 
            console.log(x)    // 这里 x 未定义
        }, 10)
    }catch(err){
        console.log('try err')
    }
    
    // 控制台输出:
    //window err
    

    最后,可能还会有一些理解上的错误,希望大家多多指正。

  • 相关阅读:
    混jdon坛子的一些思考
    Jforum论坛安装
    自己对组件化模块的一点思路.
    自己对组件化模块的一点思路.
    总结(1)
    总结(1)
    Jforum论坛安装
    Windows 下 用 php 【mail()】函数发送邮件 (本地环境)
    使用TCP/IP协议实现聊天程序
    在vista上安装delphi7的解决方案
  • 原文地址:https://www.cnblogs.com/CccZss/p/8616356.html
Copyright © 2011-2022 走看看