异步编程中的异常处理

此文为转载的,感谢原文作者——syaning!

一、引言

一般情况下,我们会使用try..catch..来进行异常处理,例如:

function sync() {
    throw new Error('sync error');
}

try {
    sync();
} catch (err) {
    console.log('error caught:', err.message);
}

// error caught: sync error

然而对于异步函数,try..catch..就无法得到想要的效果了,例如:

function async() {
    setTimeout(function() {
        throw new Error('async error');
    }, 1000);
}

try {
    async();
} catch (err) {
    console.log('error caught:', err.message);
}

// Uncaught Error: async error

这是因为异步调用是立即返回的,因此当发生异常的时候,已经脱离了try..catch..的上下文了,所以异常无法被捕获。

二、实现

下面来介绍异步编程下几种常用的异常处理方法。

1. callback

通过回调函数可以比较方便地进行异常处理,例如:

function async(callback, errback) {
    setTimeout(function() {
        var rand = Math.random();
        if (rand < 0.5) {
            errback('async error');
        } else {
            callback(rand);
        }
    }, 1000);
}

async(function(result) {
    console.log('scucess:', result);
}, function(err) {
    console.log('fail:', err);
});

这里通过setTimeout来模拟实现了一个异步函数async,该函数可能调用成功,也可能调用失败抛出异常。async接收两个参数,当调用成功时callback会被执行,当抛出异常时errback会被执行。

有时候为了方便,也会将callback和errback合并为一个回调函数,这也是Node风格回调处理。代码如下:

function async(callback) {
    setTimeout(function() {
        var rand = Math.random();
        if (rand < 0.5) {
            callback('async error');
        } else {
            callback(null, rand);
        }
    }, 1000);
}

async(function(err, result) {
    if (err) {
        console.log('fail:', err);
    } else {
        console.log('success:', result);
    }
});

这里err作为回调函数的第一个参数,如果async调用成功,则err为一个falsy值(可以是null或undefined等,在该例子中使用null);如果async调用失败,则err为抛出的异常。当调用回调函数的时候,先判断err是否为falsy。如果为falsy,则执行成功的回调;否则进行异常处理(这里原文是错误的,刚好逻辑相反了,我估计是作者不小心写错了)

然而在多异步串行的情况下,使用回调函数的方式,就会出现所谓的回调金字塔问题,代码可读性也会大打折扣。例如:

function async(callback) {
    setTimeout(function() {
        var rand = Math.random();
        if (rand < 0.2) {
            callback('async error');
        } else {
            callback(null, rand);
        }
    }, 1000);
}

async(function(err, result) {
    if (err) {
        console.log('fail:', err);
    } else {
        console.log('success:', result);
        async(function(err, result) {
            if (err) {
                console.log('fail:', err);
            } else {
                console.log('success:', result);
                async(function(err, result) {
                    if (err) {
                        console.log('fail:', err);
                    } else {
                        console.log('success:', result);
                    }
                });
            }
        });
    }
});

在这里,回调函数一层嵌一层,而且每一层都要判读是否出错。不仅代码可读性差,维护起来也非常不方便。

2. promise

上面的例子,使用promise的话,代码如下:

function async() {
    return new Promise(function(resolve, reject) {
        setTimeout(function() {
            var rand = Math.random();
            if (rand < 0.5) {
                reject('async error');
            } else {
                resolve(rand);
            }
        }, 1000);
    });
}

async().then(function(result) {
    console.log('success:', result);
}, function(err) {
    console.log('fail:', err);
});

或者使用catch的方式,代码如下:

function async() {
    return new Promise(function(resolve, reject) {
        setTimeout(function() {
            var rand = Math.random();
            if (rand < 0.5) {
                reject('async error');
            } else {
                resolve(rand);
            }
        }, 1000);
    });
}

async().then(function(result) {
    console.log('success:', result);
}).catch(function(err) {
    console.log('fail:', err);
});

使用promise的好处是执行流程直观,但是理解起来比回调函数要麻烦一些。

对于多异步操作串行的问题,使用promise的方式会使得代码简洁优雅,可读性也很强。代码如下:

function async() {
    return new Promise(function(resolve, reject) {
        setTimeout(function() {
            var rand = Math.random();
            if (rand < 0.2) {
                reject('async error');
            } else {
                resolve(rand);
            }
        }, 1000);
    });
}

function onResolved(result) {
    console.log('success:', result);
}

function onRejected(err) {
    console.log('fail:', err);
}

async().then(onResolved)
.then(async)
.then(onResolved)
.then(async)
.then(onResolved)
.catch(onRejected);

3. domain

在Node中,有一个domain模块(在io.js中该模块已经标记为deprecated),可以用来处理异步操作异常。示例代码如下:

var domain = require('domain');

function async(callback) {
    setTimeout(function() {
        var rand = Math.random();
        if (rand < 0.5) {
            throw 'async error';
        } else {
            callback(rand);
        }
    }, 1000);
}

var d = domain.create();
d.on('error', function(err) {
    console.log('fail:', err);
});
d.run(function() {
    async(function(result) {
        console.log('success:', result);
    });
});

Domain类继承自EventEmitter,所以它本质上就是一个事件发生器。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Java异步编程,出现异常是很常见的情况。异步编程的特点是任务的执行和结果的返回是分离的,因此异常处理也需要特别注意。以下是一些Java异步异常处理的建议: 1. 使用CompletableFuture的exceptionally方法处理异常。CompletableFuture是Java 8新增的异步编程工具,它提供了exceptionally方法来处理异常情况。当Future执行过程抛出异常时,exceptionally方法会捕获异常并返回一个新的Future对象,该对象的结果为指定的默认值或者提供的异常信息。代码示例: ``` CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> { // 执行异步任务 return 1 / 0; }).exceptionally(ex -> { // 异常处理 System.out.println("异步任务执行出错:" + ex.getMessage()); return 0; }); ``` 2. 使用CompletableFuture的handle方法处理异常。handle方法和exceptionally方法类似,不同的是handle方法可以处理正常结果和异常结果。当Future执行过程抛出异常时,handle方法会捕获异常并返回一个新的Future对象,该对象的结果为指定的默认值或者提供的异常信息。代码示例: ``` CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> { // 执行异步任务 return 1 / 0; }).handle((result, ex) -> { if (ex != null) { // 异常处理 System.out.println("异步任务执行出错:" + ex.getMessage()); return 0; } else { // 正常结果处理 return result; } }); ``` 3. 使用try-catch语句捕获异常。在异步编程,任务的执行往往是在另外一个线程进行的,因此try-catch语句可能无法捕获异常。如果需要在异步任务捕获异常,可以使用CompletableFuture的completeExceptionally方法抛出异常。代码示例: ``` CompletableFuture<Integer> future = new CompletableFuture<>(); CompletableFuture.runAsync(() -> { try { // 执行异步任务 int result = 1 / 0; future.complete(result); } catch (Exception ex) { // 异常处理 future.completeExceptionally(ex); } }); ``` 需要注意的是,如果在异步任务抛出异常,需要使用completeExceptionally方法抛出异常,否则异步任务的返回值将被视为正常结果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值