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

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

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

请看以下代码

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);
        }
    }
}

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

总结

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

相关推荐
她说可以呀6 小时前
Vue插槽
前端·javascript·vue.js
qq_448011167 小时前
Vue + Jinja2两套模版与法冲突造成undefinedError
前端·javascript·vue.js
FRing039 小时前
JavaScript入门到精通三十二(深拷贝、浅拷贝、递归函数)
开发语言·前端·javascript
兰亭妙微UI设计公司11 小时前
兰亭妙微ui设计公司分享:B端组件命名如何支持团队协作
前端·javascript·vue.js
小大宇12 小时前
Vue知识点-ElementPlus-03提示框+询问+通知
前端·javascript·vue.js
前端技术官13 小时前
前端的技术判断力是怎么练出来的?
javascript·web前端·localstorage·debounce·弱网处理
程序员大阳14 小时前
副队长JavaScript教程(60)-BOM 初识:window 对象与浏览器模型
javascript·bom·window对象·浏览器模型
web打印社区14 小时前
Chrome-kiosk-printing能上生产吗
开发语言·前端·javascript·chrome·websocket
志尊宝15 小时前
AI原生前端开发(041):AI辅助的状态管理(Zustand/Pinia)
前端·javascript·ai-native
Geoffwo15 小时前
DSH-SKILL基础
javascript