为什么回调函数不是一种好的异步编程方式

为什么对于异步编程,回调不是一种合适的方式,原因在于以下两点:

回调表示异步的方式是非线性的,非顺序的,很难理解

请看以下代码

javascript 复制代码
// 实际实现的复杂回调代码
function registerUser(username, password, callback) {
    // 1. 先检查用户名是否存在
    checkUsernameExists(username, function(err, exists) {
        if (err) {
            return callback(new Error("检查用户名失败: " + err.message));
        }
        
        // 条件判断:如果用户名已存在
        if (exists) {
            return callback(new Error("用户名已存在"));
        }
        
        // 2. 创建新用户
        createUser(username, password, function(err, user) {
            if (err) {
                // 错误处理:创建用户失败
                return callback(new Error("创建用户失败: " + err.message));
            }
            
            // 3. 发送欢迎邮件
            sendWelcomeEmail(user.email, function(err, emailResult) {
                if (err) {
                    // 错误处理:邮件发送失败,需要回滚!
                    console.error("邮件发送失败,回滚用户创建");
                    
                    // 回滚:删除已创建的用户
                    rollbackCreateUser(user.id, function(rollbackErr) {
                        if (rollbackErr) {
                            // 更复杂的错误:回滚也失败了!
                            console.error("回滚失败:", rollbackErr);
                        }
                        
                        // 最终回调,包含原始错误
                        return callback(new Error("发送欢迎邮件失败: " + err.message));
                    });
                    return; // 注意:这里必须 return!
                }
                
                // 4. 记录注册日志(可选操作,但不是必需的)
                if (shouldLogRegistration()) {  // 条件判断
                    logRegistration(user.id, function(err, logResult) {
                        if (err) {
                            // 注意:这里日志失败不影响主流程
                            console.warn("记录日志失败,但继续流程:", err);
                        }
                        
                        // 最终成功回调
                        callback(null, { 
                            user: user, 
                            emailSent: true,
                            logged: !err
                        });
                    });
                } else {
                    // 最终成功回调(不走日志分支)
                    callback(null, { 
                        user: user, 
                        emailSent: true,
                        logged: false
                    });
                }
            });
        });
    });
}

这段代码需要在多个嵌套函数中来回跳跃,而且错误处理代码被分散在了多个层级中,导致这段代码难读之一在于嵌套,之一在于if-else导致代码逻辑进入不同的函数处理中打断了线性的阅读

回调函数的信任问题

回调函数是控制反转的,在使用第三方函数的时候,回调函数把自己函数的一部分执行控制交给第三方函数本身

javascript 复制代码
ajax("url...", function () {
    //逻辑处理
})

当第三方函数如这里的ajax,并没有调用你的回调函数,或者说它并不是你期望的那样是异步的,又或者它把你打断函数比期望中的多调用或少嗲用了几次,都会导致程序出错,但是基于信任问题,我们可以特定的代码去解决一些问题,

调用超时

javascript 复制代码
function timeoutify(fn, delay) {
    var intv = setTimeout(function () {
        intv = null;
        fn(new Error("Timeout !"))
    }, delay);
    
    return function () {
        if (intv) {
            //没有超时
            clearTimeout(intv);
            fn.apply(this,[null].concat([].slice.apply(argument, 0)));
        }
    }
}

function foo (err, data) {
    if (err) {
        console.error(err);
    } else {
        console.log(data);
    }
}

ajax("url...", timeoutify(foo, 500));

调用过早

当不确定使用的api是否会不会永远的异步执行的时候使用

javascript 复制代码
function asyncify (fn) {
    var origin_fn = fn,
        intv = setTimeout(function () {
            intv = null;
            if (fn) fn();
        }, 0);
    fn = null;
    return function () {
        if (intv) {
            //调用过早
            var args = [].slice.call(argument);
            var bindArgs = [this].concat(args);
            
            fn = origin_fn.bind.apply(origin_fn, bindArgs);
            
            
        } else {
            //已经是异步
            orgin_fn.apply(this, argument);
        }
    }
}

这段代码最终能保证你的代码是异步执行的不管在什么情景下,所以尽管可以通过代码去解决一部分的回调信任问题,但是写这种代码的难度要高于业务本身的水平,而且代码也变得更加臃肿和难维护了

总结

基于上述,我们可以知道,为什么异步编程使用回调不是一种好的办法,一部分是回调的表示方式是非线性的非顺序的,一部分是回调函数的控制反转导致的信任问题。

相关推荐
切糕师学AI9 分钟前
Vue 中如何修改地址栏参数并重新加载?
前端·javascript·vue.js
软弹9 分钟前
Vue3如何融合TS
前端·javascript·vue.js
2501_920931707 小时前
React Native鸿蒙跨平台采用ScrollView的horizontal属性实现横向滚动实现特色游戏轮播和分类导航
javascript·react native·react.js·游戏·ecmascript·harmonyos
东东5169 小时前
智能社区管理系统的设计与实现ssm+vue
前端·javascript·vue.js·毕业设计·毕设
catino9 小时前
图片、文件的预览
前端·javascript
2501_9209317010 小时前
React Native鸿蒙跨平台实现推箱子游戏,完成玩家移动与箱子推动,当所有箱子都被推到目标位置时,玩家获胜
javascript·react native·react.js·游戏·ecmascript·harmonyos
AI老李11 小时前
PostCSS完全指南:功能/配置/插件/SourceMap/AST/插件开发/自定义语法
前端·javascript·postcss
方也_arkling11 小时前
Element Plus主题色定制
javascript·sass
2601_9498095911 小时前
flutter_for_openharmony家庭相册app实战+我的Tab实现
java·javascript·flutter
Up九五小庞12 小时前
开源埋点分析平台 ClkLog 本地部署 + Web JS 埋点测试实战--九五小庞
前端·javascript·开源