使用Promises进行顺序处理

时间:2015-12-22 12:57:51

标签: javascript node.js promise es6-promise rsvp.js

如果下面的问题是愚蠢的话,我对Node.js和JavaScript相对较新 - 请原谅。 对我来说,异步处理的承诺是有道理的,但在串行/顺序处理方面,我并不是100%肯定使用promises。让我们看一个例子(伪代码):

目标:读取文件,处理从文件中读取的内容并发送通知 使用HTTP post post。

bendUniverseWithoutPromise: function() {

   var data = fs.readFileSync(..); //Read the file
   var result = processData(data);
   this.postNotification(data);

}

在上面的函数中,在我们读取文件之前,processData()无法运行。在我们完成处理之前,我们无法发送通知。

让我们看一个稍微不同的版本(假设上面的每个方法调用都返回一个promise或者我们将它们包装在一个promise中):

    bendUniverseWithPromise: function() {
      return new Promise(function() {
         fs.readFileAsync(fileName)
            .then(processData(data))
            .then(postNotification(result))
        })          
    }

现在,我的问题是:

  1. 看到我们在这个例子中需要串行/顺序处理,promise版本如何比非promise版本更好?它比第一个例子做得更好?也许这是一个不好的例子,但那么什么是展示差异的好例子呢?
  2. 除了语法之外,promise版本在代码的可读性方面增加了一点(只是一点点),并且可以通过嵌套的promises,context(this!)等变得非常复杂。
  3. 我确实理解,从技术上讲,第一种方法将不会返回,直到所有处理完成,第二种方法将立即返回,并且处理虽然仍然是顺序的(在方法的上下文中),但仍将在后台进行。
  4. 是否有关于使用承诺的一般规则?有没有图案和反模式?
  5. 提前谢谢。

2 个答案:

答案 0 :(得分:2)

我将通过进一步举例来尝试回答你的所有四点。

让我们说第一个操作(文件读取)是一个缓慢的I / O绑定操作,需要900毫秒。处理和通知分别受CPU限制和I / O限制,每个采用50 ms。 What do the terms “CPU bound” and “I/O bound” mean?

现在,两个版本都将花费相同的1000毫秒来完成,但第二个示例更好地利用了可用资源,因为它是异步的。这是基于承诺的版本的优点。第一个版本将使服务器完全没有响应一整秒,而第二个版本只会使服务器在50 ms CPU绑定处理步骤期间无响应。

当我们考虑其中10个请求同时进入时,这有望变得更加清晰。第一个例子一次一个地通过它们,在1s之后提供请求#1,在2s之后提供#2,依此类推,在10s之后完成。其平均性能为1 req / s。第二个版本将启动对请求#1的文件读取,然后立即继续请求#2,启动另一个文件读取,依此类推所有请求。然后,所有请求将在大约1秒内完成读取,假设开销为100毫秒,磁盘读取带宽很少或没有饱和。然后处理将排队,对所有请求总共花费500毫秒。最后,我们可以并行进行通知发布,因为它再次受I / O限制。然后,在这个理想化的示例中,所有请求将在大约1.5秒内完成,超过6 req / s,性能提高6倍。这完全是由于异步性提供了更好的资源。

因此,规则是在执行I / O绑定工作时始终使用async / promises

旁注:

您的第二个示例不正确,因为该范围中没有定义dataresult变量,正确的版本只会将函数传递给then()

bendUniverseWithPromise: function (fileName) {
  return fs.readFileAsync(fileName)
           .then(processData)
           .then(postNotification)          
}

答案 1 :(得分:1)

首先让我们更正异步代码:

bendUniverseWithPromise: function() {
  return fs.readFileAsync(fileName)
           .then(processData)
           .then(postNotification);
}

现在,上面的是(差不多,已经完成)反模式 - explicit construction anti-pattern

至于为什么你想要使用承诺的版本,那么它是异步的 ..它允许在等待异步(主要是I / O)操作时进行其他操作。

注意:fs.readFileAsync默认情况下不会返回承诺,需要“保证”。