Inhaltsverzeichnis
回调地狱或末日金字塔
解决方案1:回调命名和模块化
结构
帮助者
执行登录
检查用户名
检查密码
获取用户相关数据
最后的修饰和使用
解决方案 2:Promise
Q简而言之
准备承诺
检查错误
解决方案 3:步骤库
选择哪一个?
模块化:
承诺(Q):
步骤库:
结论
Heim Web-Frontend js-Tutorial Node.js: Bewältigung der Herausforderungen der asynchronen Ausführung

Node.js: Bewältigung der Herausforderungen der asynchronen Ausführung

Aug 31, 2023 pm 01:45 PM

Node.js: Bewältigung der Herausforderungen der asynchronen Ausführung

Node.js 允许您快速轻松地创建应用程序。但由于其异步特性,可能很难编写可读且可管理的代码。在本文中,我将向您展示一些如何实现这一目标的技巧。


回调地狱或末日金字塔

Node.js 的构建方式强制您使用异步函数。这意味着回调,回调,甚至更多回调。您可能已经看到过,甚至自己编写过这样的代码:

app.get('/login', function (req, res) {
	sql.query('SELECT 1 FROM users WHERE name = ?;', [ req.param('username') ], function (error, rows) {
		if (error) {
			res.writeHead(500);
			return res.end();
		}
		if (rows.length < 1) {
			res.end('Wrong username!');
		} else {
			sql.query('SELECT 1 FROM users WHERE name = ? && password = MD5(?);', [ req.param('username'), req.param('password') ], function (error, rows) {
				if (error) {
					res.writeHead(500);
					return res.end();
				}
				if (rows.length < 1) {
					res.end('Wrong password!');
				} else {
					sql.query('SELECT * FROM userdata WHERE name = ?;', [ req.param('username') ], function (error, rows) {
						if (error) {
							res.writeHead(500);
							return res.end();
						}
						req.session.username = req.param('username');
						req.session.data = rows[0];
						res.rediect('/userarea');
					});
				}
			});
		}
	});
});
Nach dem Login kopieren

这实际上是直接来自我的第一个 Node.js 应用程序的一个片段。如果您在 Node.js 中做过一些更高级的事情,您可能会理解所有内容,但这里的问题是,每次使用某些异步函数时,代码都会向右移动。它变得更难阅读,更难调试。幸运的是,有一些解决方案可以解决这个问题,因此您可以为您的项目选择合适的解决方案。


解决方案1:回调命名和模块化

最简单的方法是命名每个回调(这将帮助您调试代码)并将所有代码拆分为模块。只需几个简单的步骤即可将上面的登录示例变成一个模块。

结构

让我们从一个简单的模块结构开始。为了避免上述情况,当你只是将混乱分成更小的混乱时,让我们将其作为一个类:

var util = require('util');

function Login(username, password) {
	function _checkForErrors(error, rows, reason) {
		
	}
	
	function _checkUsername(error, rows) {
		
	}
	
	function _checkPassword(error, rows) {
		
	}
	
	function _getData(error, rows) {
		
	}
	
	function perform() {
		
	}
	
	this.perform = perform;
}

util.inherits(Login, EventEmitter);
Nach dem Login kopieren

该类由两个参数构造:用户名密码。看示例代码,我们需要三个函数:一个检查用户名是否正确(_checkUsername),另一个检查密码(_checkPassword),还有一个返回用户相关数据(_getData)并通知应用程序登录成功。还有一个 _checkForErrors 帮助器,它将处理所有错误。最后,有一个 perform 函数,它将启动登录过程(并且是类中唯一的公共函数)。最后我们继承EventEmitter来简化该类的使用。

帮助者

_checkForErrors 函数将检查是否发生任何错误或 SQL 查询是否未返回任何行,并发出相应的错误(以及提供的原因):

function _checkForErrors(error, rows, reason) {
	if (error) {
		this.emit('error', error);
		return true;
	}
	
	if (rows.length < 1) {
		this.emit('failure', reason);
		return true;
	}
	
	return false;
}
Nach dem Login kopieren

它还会返回 truefalse,具体取决于是否发生错误。

执行登录

perform 函数只需执行一个操作:执行第一个 SQL 查询(检查用户名是否存在)并分配适当的回调:

function perform() {
	sql.query('SELECT 1 FROM users WHERE name = ?;', [ username ], _checkUsername);
}
Nach dem Login kopieren

我假设您的 SQL 连接可以在 sql 变量中全局访问(只是为了简化,讨论这是否是一个好的实践超出了本文的范围)。这就是这个函数的全部内容。

检查用户名

下一步是检查用户名是否正确,如果正确,则触发第二个查询 - 检查密码:

function _checkUsername(error, rows) {
	if (_checkForErrors(error, rows, 'username')) {
		return false;
	} else {
		sql.query('SELECT 1 FROM users WHERE name = ? && password = MD5(?);', [ username, password ], _checkPassword);
	}
}
Nach dem Login kopieren

与混乱示例中的代码几乎相同,但错误处理除外。

检查密码

这个函数与前一个函数几乎完全相同,唯一的区别是调用的查询:

function _checkPassword(error, rows) {
	if (_checkForErrors(error, rows, 'password')) {
		return false;
	} else {
		sql.query('SELECT * FROM userdata WHERE name = ?;', [ username ], _getData);
	}
}
Nach dem Login kopieren

获取用户相关数据

此类中的最后一个函数将获取与用户相关的数据(可选步骤)并用它触发成功事件:

function _getData(error, rows) {
	if (_checkForErrors(error, rows)) {
		return false;
	} else {
		this.emit('success', rows[0]);
	}
}
Nach dem Login kopieren

最后的修饰和使用

最后要做的事情是导出类。在所有代码后面添加这一行:

module.exports = Login;
Nach dem Login kopieren

这将使 Login 类成为该模块将导出的唯一内容。稍后可以像这样使用它(假设您已将模块文件命名为 login.js 并且它与主脚本位于同一目录中):

var Login = require('./login.js');

...

app.get('/login', function (req, res) {
	var login = new Login(req.param('username'), req.param('password));
	login.on('error', function (error) {
		res.writeHead(500);
		res.end();
	});
	login.on('failure', function (reason) {
		if (reason == 'username') {
			res.end('Wrong username!');
		} else if (reason == 'password') {
			res.end('Wrong password!');
		}
	});
	login.on('success', function (data) {
		req.session.username = req.param('username');
		req.session.data = data;
		res.redirect('/userarea');
	});
	login.perform();
});
Nach dem Login kopieren

这里又多了几行代码,但是代码的可读性增加了,非常明显。此外,该解决方案不使用任何外部库,这使得如果有新人加入您的项目,它会变得完美。

这是第一种方法,让我们继续第二种方法。


解决方案 2:Promise

使用 Promise 是解决此问题的另一种方法。承诺(正如您可以在提供的链接中阅读的那样)“表示从操作的单个完成中返回的最终值”。实际上,这意味着您可以链接调用以压平金字塔并使代码更易于阅读。

我们将使用 NPM 存储库中提供的 Q 模块。

Q简而言之

在开始之前,我先向您介绍一下Q。对于静态类(模块),我们主要使用 Q.nfcall 函数。它帮助我们将遵循 Node.js 回调模式(其中回调的参数是错误和结果)的每个函数转换为 Promise。它的使用方式如下:

Q.nfcall(http.get, options);
Nach dem Login kopieren

它非常像 Object.prototype.call。您还可以使用 Q.nfapply ,它类似于 Object.prototype.apply:

Q.nfapply(fs.readFile, [ 'filename.txt', 'utf-8' ]);
Nach dem Login kopieren

此外,当我们创建 Promise 时,我们使用 then(stepCallback) 方法添加每个步骤,使用 catch(errorCallback) 捕获错误,并使用 done() 结束。

在这种情况下,由于 sql 对象是一个实例,而不是静态类,所以我们必须使用 Q.ninvokeQ.npost ,它们与上面类似。不同之处在于,我们将方法的名称作为字符串传递到第一个参数中,并将我们想要使用的类的实例作为第二个参数传递,以避免方法从实例。

准备承诺

首先要做的是执行第一步,使用Q.nfcallQ.nfapply(使用你更喜欢的,下面没有区别):

var Q = require('q');

...


app.get('/login', function (req, res) {
	Q.ninvoke('query', sql, 'SELECT 1 FROM users WHERE name = ?;', [ req.param('username') ])
});
Nach dem Login kopieren

请注意该行末尾缺少分号 - 函数调用将被链接起来,因此它不能在那里。我们只是像混乱的示例中那样调用 sql.query ,但我们省略了回调参数 - 它由 Promise 处理。

检查用户名

现在我们可以为 SQL 查询创建回调,它将与“厄运金字塔”示例中的回调几乎相同。在 Q.ninvoke 调用后添加以下内容:

.then(function (rows) {
	if (rows.length < 1) {
		res.end('Wrong username!');
	} else {
		return Q.ninvoke('query', sql, 'SELECT 1 FROM users WHERE name = ? && password = MD5(?);', [ req.param('username'), req.param('password') ]);
	}
})
Nach dem Login kopieren

如您所见,我们使用 then 方法附加回调(下一步)。另外,在回调中我们省略了 error 参数,因为我们稍后会捕获所有错误。我们正在手动检查查询是否返回某些内容,如果是,我们将返回下一个要执行的承诺(同样,由于链接而没有分号)。

检查密码

与模块化示例一样,检查密码与检查用户名几乎相同。这应该在最后一次 then 调用之后进行:

.then(function (rows) {
	if (rows.length < 1) {
		res.end('Wrong password!');
	} else {
		return Q.ninvoke('query', sql, 'SELECT * FROM userdata WHERE name = ?;', [ req.param('username') ]);
	}
})
Nach dem Login kopieren

获取用户相关数据

最后一步是将用户数据放入会话中。再一次,回调与混乱的示例没有太大区别:

.then(function (rows) {
	req.session.username = req.param('username');
	req.session.data = rows[0];
	res.rediect('/userarea');
})
Nach dem Login kopieren

检查错误

使用 Promise 和 Q 库时,所有错误均由使用 catch 方法的回调集处理。在这里,无论错误是什么,我们都只发送 HTTP 500,如上面的示例所示:

.catch(function (error) {
	res.writeHead(500);
	res.end();
})
.done();
Nach dem Login kopieren

之后,我们必须调用 done 方法来“确保,如果错误在结束之前没有得到处理,它将被重新抛出并报告”(来自库的 README)。现在我们漂亮的扁平化代码应该如下所示(并且行为就像混乱的代码一样):

var Q = require('q');

...


app.get('/login', function (req, res) {
	Q.ninvoke('query', sql, 'SELECT 1 FROM users WHERE name = ?;', [ req.param('username') ])
	.then(function (rows) {
		if (rows.length < 1) {
			res.end('Wrong username!');
		} else {
			return Q.ninvoke('query', sql, 'SELECT 1 FROM users WHERE name = ? && password = MD5(?);', [ req.param('username'), req.param('password') ]);
		}
	})
	.then(function (rows) {
		if (rows.length < 1) {
			res.end('Wrong password!');
		} else {
			return Q.ninvoke('query', sql, 'SELECT * FROM userdata WHERE name = ?;', [ req.param('username') ]);
		}
	})
	.then(function (rows) {
		req.session.username = req.param('username');
		req.session.data = rows[0];
		res.rediect('/userarea');
	})
	.catch(function (error) {
		res.writeHead(500);
		res.end();
	})
	.done();
});
Nach dem Login kopieren

代码更加简洁,并且比模块化方法涉及的重写更少。


解决方案 3:步骤库

此解决方案与上一个解决方案类似,但更简单。 Q 有点重,因为它实现了整个 Promise 的想法。 Step 库的存在只是为了消除回调地狱。使用起来也更简单,因为您只需调用从模块导出的唯一函数,将所有回调作为参数传递,并使用 this 代替每个回调。因此,可以使用 Step 模块将这个混乱的示例转换成这样:

var step = require('step');

...

app.get('/login', function (req, res) {
	step(
		function start() {
			sql.query('SELECT 1 FROM users WHERE name = ?;', [ req.param('username') ], this);
		},
		function checkUsername(error, rows) {
			if (error) {
				res.writeHead(500);
				return res.end();
			}
			if (rows.length < 1) {
				res.end('Wrong username!');
			} else {
				sql.query('SELECT 1 FROM users WHERE name = ? && password = MD5(?);', [ req.param('username'), req.param('password') ], this);
			}
		},
		function checkPassword(error, rows) {
			if (error) {
				res.writeHead(500);
				return res.end();
			}
			if (rows.length < 1) {
				res.end('Wrong password!');
			} else {
				sql.query('SELECT * FROM userdata WHERE name = ?;', [ req.param('username') ], this);
			}
		},
		function (error, rows) {
			if (error) {
				res.writeHead(500);
				return res.end();
			}
			req.session.username = req.param('username');
			req.session.data = rows[0];
			res.rediect('/userarea');
		}
	);
});
Nach dem Login kopieren

这里的缺点是没有通用的错误处理程序。尽管在一个回调中引发的任何异常都会作为第一个参数传递给下一个回调(因此脚本不会因为未捕获的异常而停止运行),但在大多数情况下,为所有错误使用一个处理程序是很方便的。


选择哪一个?

这很大程度上是个人选择,但为了帮助您选择正确的选择,以下列出了每种方法的优缺点:

模块化:

优点:

  • 没有外部库
  • 有助于提高代码的可重用性

缺点:

  • 更多代码
  • 如果您要转换现有项目,则需要进行大量重写

承诺(Q):

优点:

  • 更少的代码
  • 如果应用于现有项目,只需稍微重写

缺点:

  • 您必须使用外部库
  • 需要一些学习

步骤库:

优点:

  • 易于使用,无需学习
  • 如果转换现有项目,则几乎可以进行复制和粘贴

缺点:

  • 没有通用的错误处理程序
  • 正确缩进 step 函数有点困难

结论

正如您所看到的,Node.js 的异步特性是可以管理的,并且可以避免回调地狱。我个人使用模块化方法,因为我喜欢让我的代码结构良好。我希望这些技巧将帮助您编写更具可读性的代码并更轻松地调试脚本。

Das obige ist der detaillierte Inhalt vonNode.js: Bewältigung der Herausforderungen der asynchronen Ausführung. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Erklärung dieser Website
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn

Heiße KI -Werkzeuge

Undresser.AI Undress

Undresser.AI Undress

KI-gestützte App zum Erstellen realistischer Aktfotos

AI Clothes Remover

AI Clothes Remover

Online-KI-Tool zum Entfernen von Kleidung aus Fotos.

Undress AI Tool

Undress AI Tool

Ausziehbilder kostenlos

Clothoff.io

Clothoff.io

KI-Kleiderentferner

AI Hentai Generator

AI Hentai Generator

Erstellen Sie kostenlos Ai Hentai.

Heißer Artikel

R.E.P.O. Energiekristalle erklärten und was sie tun (gelber Kristall)
3 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Beste grafische Einstellungen
3 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. So reparieren Sie Audio, wenn Sie niemanden hören können
3 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25: Wie man alles in Myrise freischaltet
4 Wochen vor By 尊渡假赌尊渡假赌尊渡假赌

Heiße Werkzeuge

Notepad++7.3.1

Notepad++7.3.1

Einfach zu bedienender und kostenloser Code-Editor

SublimeText3 chinesische Version

SublimeText3 chinesische Version

Chinesische Version, sehr einfach zu bedienen

Senden Sie Studio 13.0.1

Senden Sie Studio 13.0.1

Leistungsstarke integrierte PHP-Entwicklungsumgebung

Dreamweaver CS6

Dreamweaver CS6

Visuelle Webentwicklungstools

SublimeText3 Mac-Version

SublimeText3 Mac-Version

Codebearbeitungssoftware auf Gottesniveau (SublimeText3)

Wie erstelle ich meine eigenen JavaScript -Bibliotheken? Wie erstelle ich meine eigenen JavaScript -Bibliotheken? Mar 18, 2025 pm 03:12 PM

In Artikel werden JavaScript -Bibliotheken erstellt, veröffentlicht und aufrechterhalten und konzentriert sich auf Planung, Entwicklung, Testen, Dokumentation und Werbestrategien.

Wie optimiere ich den JavaScript -Code für die Leistung im Browser? Wie optimiere ich den JavaScript -Code für die Leistung im Browser? Mar 18, 2025 pm 03:14 PM

In dem Artikel werden Strategien zur Optimierung der JavaScript -Leistung in Browsern erörtert, wobei der Schwerpunkt auf die Reduzierung der Ausführungszeit und die Minimierung der Auswirkungen auf die Lastgeschwindigkeit der Seite wird.

Was soll ich tun, wenn ich auf den Codendruck auf Kleidungsstücke für Front-End-Thermalpapier-Quittungen stoße? Was soll ich tun, wenn ich auf den Codendruck auf Kleidungsstücke für Front-End-Thermalpapier-Quittungen stoße? Apr 04, 2025 pm 02:42 PM

Häufig gestellte Fragen und Lösungen für das Ticket-Ticket-Ticket-Ticket in Front-End im Front-End-Entwicklungsdruck ist der Ticketdruck eine häufige Voraussetzung. Viele Entwickler implementieren jedoch ...

Wie debugge ich den JavaScript -Code effektiv mithilfe von Browser -Entwickler -Tools? Wie debugge ich den JavaScript -Code effektiv mithilfe von Browser -Entwickler -Tools? Mar 18, 2025 pm 03:16 PM

In dem Artikel werden effektives JavaScript -Debuggen mithilfe von Browser -Entwickler -Tools, der Schwerpunkt auf dem Festlegen von Haltepunkten, der Konsole und der Analyse der Leistung erörtert.

Wie verwende ich Quellkarten zum Debuggen, um den JavaScript -Code zu debuggen? Wie verwende ich Quellkarten zum Debuggen, um den JavaScript -Code zu debuggen? Mar 18, 2025 pm 03:17 PM

In dem Artikel wird erläutert, wie Quellkarten zum Debuggen von JavaScript verwendet werden, indem er auf den ursprünglichen Code zurückgegeben wird. Es wird erläutert, dass Quellenkarten aktiviert, Breakpoints eingestellt und Tools wie Chrome Devtools und WebPack verwendet werden.

Wie benutze ich Javas Sammlungsrahmen effektiv? Wie benutze ich Javas Sammlungsrahmen effektiv? Mar 13, 2025 pm 12:28 PM

In diesem Artikel wird der effektive Gebrauch des Sammlungsrahmens von Java untersucht. Es betont die Auswahl geeigneter Sammlungen (Liste, Set, Karte, Warteschlange) basierend auf Datenstruktur, Leistungsanforderungen und Thread -Sicherheit. Optimierung der Sammlungsnutzung durch effizientes Gebrauch

TypeScript für Anfänger, Teil 2: Grundlegende Datentypen TypeScript für Anfänger, Teil 2: Grundlegende Datentypen Mar 19, 2025 am 09:10 AM

Sobald Sie das Typscript-Tutorial für Einstiegsklasse gemeistert haben, sollten Sie in der Lage sein, Ihren eigenen Code in eine IDE zu schreiben, die TypeScript unterstützt und in JavaScript zusammenfasst. Dieses Tutorial wird in verschiedenen Datentypen in TypeScript eingetaucht. JavaScript hat sieben Datentypen: NULL, UNDEFINED, BOOLEAN, NUMMER, STRING, SYMBOL (durch ES6 eingeführt) und Objekt. TypeScript definiert mehr Typen auf dieser Grundlage, und dieses Tutorial wird alle ausführlich behandelt. Null -Datentyp Wie JavaScript, null in TypeScript

Erste Schritte mit Chart.js: Kuchen-, Donut- und Bubble -Diagramme Erste Schritte mit Chart.js: Kuchen-, Donut- und Bubble -Diagramme Mar 15, 2025 am 09:19 AM

In diesem Tutorial wird erläutert, wie man mit Diagramm.js Kuchen-, Ring- und Bubble -Diagramme erstellt. Zuvor haben wir vier Chart -Arten von Charts gelernt. Erstellen Sie Kuchen- und Ringdiagramme Kreisdiagramme und Ringdiagramme sind ideal, um die Proportionen eines Ganzen anzuzeigen, das in verschiedene Teile unterteilt ist. Zum Beispiel kann ein Kreisdiagramm verwendet werden, um den Prozentsatz der männlichen Löwen, weiblichen Löwen und jungen Löwen in einer Safari oder den Prozentsatz der Stimmen zu zeigen, die verschiedene Kandidaten bei der Wahl erhalten. Kreisdiagramme eignen sich nur zum Vergleich einzelner Parameter oder Datensätze. Es ist zu beachten, dass das Kreisdiagramm keine Entitäten ohne Wert zeichnen kann, da der Winkel des Lüfters im Kreisdiagramm von der numerischen Größe des Datenpunkts abhängt. Dies bedeutet jede Entität ohne Anteil

See all articles