为什么在同步功能上实现异步?

时间:2018-09-11 04:34:31

标签: javascript node.js asynchronous

我是NodeJS的新手,我发现使用异步功能有些困难。我想在这里找出好处。以下是我对异步功能的理解。

能否请您在下面验证我的所有理解?

  1. 当要执行独立操作(来自主程序流)时,可以使用异步功能。当主程序非常需要来自异步功能的数据/响应或将各种独立的异步功能互连时,使用异步功能是不理想的。
  2. 在主程序流程中依赖async函数的输出结果是不好的。因为异步总是在主流程之后执行。因此,如果您需要在主流中执行某些功能,则最好将其定义为“同步”而不是“异步”。
  3. 调用独立异步函数时,通常的做法是使用Promise或回调函数来调用后续操作(异步函数)。
  4. 我仍然可以在Async函数中调用同步函数。但是,如果我从同步函数调用异步函数,则该程序可能无法按预期方式工作,因为异步函数只会在最后执行?

3 个答案:

答案 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__Ath__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错误。