9月阅读周·JavaScript异步编程设计快速响应的网络应用:深入理解JavaScript事件,嵌套式回调的解嵌套篇
背景
去年下半年,我在微信书架里加入了许多技术书籍,各种类别的都有,断断续续的读了一部分。
没有计划的阅读,收效甚微。
新年伊始,我准备尝试一下其他方式,比如阅读周。每月抽出1~2个非连续周,完整阅读一本书籍。
这个“玩法”虽然常见且板正,但是有效,已经坚持阅读七个月。
已读完书籍:《架构简洁之道》、《深入浅出的Node.js》、《你不知道的JavaScript(上卷)》、《你不知道的JavaScript(中卷)》、《你不知道的JavaScript(下卷)》、《数据结构与算法JavaScript描述》、《WebKit技术内幕》、《前端架构:从入门到微前端》、《秒懂算法:用常识解读数据结构与算法》、《JavaScript权威指南》。
当前阅读周书籍:《JavaScript异步编程设计快速响应的网络应用》。
嵌套式回调的解嵌套
JavaScript 中最常见的反模式做法是,回调内部再嵌套回调。还记得前言里提到的金字塔厄运吗?我们先来看一个具体的例子,你也可能在Node服务器上看到过类似的代码。
function checkPassword(username, passwordGuess, callback) {
var queryStr = 'SELECT * FROM user WHERE username =?';
db.query(queryStr, username, function (err, result) {
if (err) throw err;
hash(passwordGuess, function(passwordGuessHash) {
callback(passwordGuessHash === result['password_hash']);
});
});
}
这里定义了一个异步函数 checkPassword,它触发了另一个异步函数db.query,而后者又可能触发另外一个异步函数hash。(在阅读代码之前,无法确认这些函数是否真的异步,但这里的几个函数理应如此。)
这段代码有什么问题呢?目前为止,没有任何问题。它能用,而且简洁明了。但是,如果试图向其添加新特性,它就会变得毛里毛躁、险象环生,比如去处理那个数据库错误,而不是抛出错误(请参阅1.4.3节)、记录尝试访问数据库的次数、阻塞访问数据库,等等。
嵌套式回调诱惑我们通过添加更多代码来添加更多特性,而不是将这些特性实现为可管理、可重用的代码片段。checkPassword 有一种可以避免出现上述苗头的等价实现方式,如下:
function checkPassword(username, passwordGuess, callback) {
var passwordHash;
var queryStr = 'SELECT * FROM user WHERE username =?';
db.query(qyeryStr, username, queryCallback);
function queryCallback(err, result) {
if (err) throw err;
passwordHash = result['password_hash'];
hash(passwordGuess, hashCallback);
}
function hashCallback(passwordGuessHash) {
callback(passwordHash === passwordGuessHash);
}
}
这种写法更啰嗦一些,但读起来更清晰,也更容易扩展。由于这里赋予了异步结果(即passwordHash)更宽广的作用域,所以获得了更大的灵活性。
按照惯例,请避免两层以上的函数嵌套。关键是找到一种在激活异步调用之函数的外部存储异步结果的方式,这样回调本身就没有必要再嵌套了。
总结
JavaScript中存在一种多线程性:可以孵化出Worker进程。每个孵化出的进程都可以与其他进程交换数据,其限制等同于任何其他I/O进程。Worker对象使得我们有可能利用多个内核,同时不会破坏JavaScript的游戏规则(代码不可能被中断;变量只有处于其作用域内部时才是可访问的)。
PubSub 模式是一种将回调赋值给已命名事件的回调组织方式,而Promise对象是一种表示一次性事件的直观对象。
作者介绍
非职业「传道授业解惑」的开发者叶一一。
《趣学前端》、《CSS畅想》等系列作者。华夏美食、国漫、古风重度爱好者,刑侦、无限流小说初级玩家。
如果看完文章有所收获,欢迎点赞👍 | 收藏⭐️ | 留言📝。
- 点赞
- 收藏
- 关注作者
评论(0)