我是NodeJS的新手,我发现使用异步功能有些困难。我想在这里找出好处。以下是我对异步功能的理解。
能否请您在下面验证我的所有理解?
答案 0 :(得分:4)
好的,让我们逐一研究它。作为基础,请记住,Node.js是一个单线程进程,并且如果它需要执行blocking processes
(例如读取文件)(创建事件指针,读取文件句柄,设置文件路径,设置打开模式等),最好使用异步函数,该函数在同一页或线程s / pool的单独线程上执行。
1)当独立运行时,异步函数很好用 (从主程序流)要执行的操作。不理想 当来自服务器的数据/响应时使用异步功能 主程序非常需要异步功能,或者 当各种独立的异步函数是 互连的。
对于初学者来说,我们不会将程序文件称为main program
,因为Node.js世界中没有子程序(我不是在谈论模块和喜欢)。
现在,当您说当需要立即输出时,不应该使用任何异步函数时,您实际上是对的。让我们来看下面的例子:
...
const data = readFile( 'fileName', ( data, output ) => {
...
} );
console.log( data ); // this might be null or undefined
在上述情况下,我们将不使用异步功能(传统意义上)。但是,在ES6及更高版本中,我们得到了可爱的async/await
范例:
const data = await readFile( 'filename' );
await
使调用伪同步:其行为类似于async
函数,但是会有一个暂停的线程等待输出。因此,在这里,您绝对正确!让我们继续前进。
2)依赖于异步输出的结果是不好的 功能在主程序流程中。因为异步总是在之后执行 主要流程。因此,如果您需要在其中执行某些功能 主流程,将其定义为同步,而不是异步是很好的。
在这里,您说async
在主流之后运行。现在,那个是不正确的。让我来简单描述一下线程评估和执行:
说,有两个sync
函数A()
和B()
,并且它们对应的线程分别是th__A
和th__B
,它们将变成这样:
th__a ---> th__b
如果按A()
的顺序射击,则B()
。它等待对第一个同步(或阻塞)过程的评估,然后执行第二个。显而易见,不是在整个执行结束之后。
但是,如果它们现在是异步函数,它们将并行执行。假设A()
是一个同步函数,而B()
是一个异步函数,具有与上面相同的线程名称,执行是这样的:
th__a ----
- - th__b ->
其中-
代表一个时钟周期,->
代表执行结束。我们可以看到触发了A()
,然后在新线程上触发了B()
。
我想这很有道理。现在,再次返回,如果您需要在异步调用时立即使用它们,请使用await
。
3)调用独立异步函数时,这是一种常见的做法 使用promise或调用后续操作(异步函数) 回调。
绝对。
说,我们定义一个函数sayHello()
:
const sayHello = () => {
const P = Q.defer();
// P.resolve(data);
// or if there is an exception
// P.reject(error);
return p.promise;
};
其中Q
是出色的Promise库。我们可以这样称呼它:
sayHello.then( ( data ) => {
console.log( data ); // P.resolve(..) is working here since the promise was successful.
} ).catch( ( err ) => {
console.log( err ); // P.reject(..) is working here since there was a problem.
} );
或者您可以使用像fs.readFile(...)
这样的回调:
fs.readFile( 'fileName', ( e, data ) => {
if( e ) { return console.log( e ); } // error was handled
// perform subsequent functions here with data
} );
4)我仍然可以在Async函数中调用sync函数,但是 如果从Async函数调用Async函数,则程序可能无法按预期工作 同步功能/操作,因为异步功能将仅执行 最后?
不是。参见点(2)。这与线程有关。不是静态过程。您可以在async函数中很好地调用sync函数,它会完美运行。
当您读取文件时,说您想用\n
或换行来分割数据:
...
if( e ) { return console.log( e ); }
const dataLines = e.split( '\n' ); // this will work brilliantly
...
我希望这可以使一切变得清楚! :)
答案 1 :(得分:1)
似乎您尚未研究javascript事件循环。 Here是一篇不错的文章,您可以关注。通常在javascript中,异步功能用于避免进程(如浏览器上的HTTP调用,在NodeJS上读取文件)的IO(输入/输出)延迟,而不是执行延迟或并行执行,因为javascript进程是单线程的。如果您想研究有关无阻塞IO的更多信息,Here是我写过的有关其工作原理的文章,似乎可以回答您所有的问题。
在编写异步方法时,正如您提到的,很难遵循每个回调,然后再次执行一次回调,然后一次又一次地在JavaScript中称为回调地狱。 await
async
不仅仅是一个简单的解决方案。
答案 2 :(得分:1)
当处理网络请求和/或I / O操作(或可能花费很长时间的事情)时,javascript / node应用程序中异步功能的实际使用就发挥了作用。
网络通话示例
以聊天应用程序为例。客户端(您)向某人A发送一条消息。该消息被发送到服务器或直接发送给收件人(p2p),以根据通信模型进行进一步处理。您还希望将其他消息发送给其他人。但是,如果不是异步处理请求,则客户端(您)可能必须等待不确定的时间;取决于您的网络速度,收件人的网络速度,网络超时期限或任何其他负责的因素。
考虑到您必须等待2个时间单位才能完成请求
time: 1 2 3 4 5
action: SendToA(message) waitA waitA ResponseFromA SendToB(message)
但是如果同一请求是异步的。您发送由客户端(用于p2p通信)或由客户端和服务器(对于客户端/服务器)两者进行异步处理的请求。这就是浏览器中实际发生的情况。浏览器以异步方式处理请求,因此在您等待请求完成时,浏览器代码的任何其他部分都不会卡住。
time: 1 2 3 4 5
action: SendToA(message) waitA/SendToB(message) waitA/waitB ResponseFromA ResponseFromB
因此,在同步情况下,您只会在5个时间单位内获得一个成功请求,而在异步情况下,您将在5个时间单位内获得两个请求成功。
注释:实际的实现可能会延迟时间单位,这只是对实际发生情况的大致了解。
I / O读/写示例
I / O读/写与网络调用非常相似;即需要时间才能完成。假设您的应用程序有两个表,这些表从两个不同的文件读取并填充每个表。如果您同时处理两个请求,则整体执行会大大延迟,而异步会有所帮助。
要进行同步:
time: 1 2 3 4 5
action: ReadFromFileA(message) waitA waitA ResponseFromA ReadFromFileB(message)
对于异步:
time: 1 2 3 4 5
action: ReadFromFileA() ReadFromFileB() waitA/waitB ResponseFromA ResponseFromB
这是异步调用/函数可以做什么的非常笼统的解释。但是,异步并不是总是要做的事情。正如您在问题中提到的,有时候确实需要同步调用/功能。
打印是必须同步的非常特殊的操作。您不能异步打印两张纸。
从属功能也必须同步。
所有这些说明,如果正确使用异步,那么异步是一项非常强大的功能。实时应用中几乎总是有异步性的可能性。如果您的应用程序不使用它,则您的用户可能会抱怨您的应用程序中的UX错误。