Javascript Promise node.js? [英] Javascript Promise node.js?

查看:69
本文介绍了Javascript Promise node.js?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是一个node.js新手,我试图理解如何以非阻塞的方式组织一些逻辑节点喜欢它。

I'm a node.js newbie and I'm trying to understand how I can organize some logic in the non-blocking way node likes it.

I有一套环境['stage','prod'],另一组参数叫做品牌['A','B','C']和一套设备['phone','tablet']。

I have a set of environments ['stage','prod'], and another set of parameters called brands ['A','B','C'] and a set of devices ['phone','tablet'].

在节点回调驱动的世界中,我有:

In node's callback-driven world I have this:

brands.forEach( function(brand) {
    devices.forEach( function(device) {
        var tapeS = getTape('stage',brand,device); // bad example...tapeS never set
        var tapeP = getTape('prod' ,brand,device);
    })
} )
// more stuff here
function getTape(env,brand,device) {
   var req = http.request(someOptions,function(resp) {
       // ok, so we handle the response here, but how do I sequence this with all the other
       // responses, also happening asynchronously?
   });
}

我正在尝试为每个环境构建一个包含块的报告:

I'm trying to build a report with blocks for each environment:

A:
    Stage -- report
    Prod  -- report 
B:    ...

我的问题是,因为这里的所有内容都是异步的,特别是在调用node的http.request的getTape中。如何在所有这些异步奇迹结束时序列化所有内容,以便按照我想要的顺序创建报告?

My problem is that since everything here is so async, especially inside getTape, which calls node's http.request. How can I serialize everything at the end of all this async wonderment so I can create the report in the order I want?

我听说过有关javascript Promises的内容。那会有所帮助吗,即收集所有这些Promise的某种方式然后等待它们全部完成,然后获取它们收集的数据?

I heard something about javascript Promises. Would that help, i.e. some way to collect all these Promises then wait for them all to complete, then get the data they collected?

推荐答案

Q 是node.js中的主要承诺实现。我也有自己的超轻量级承诺库承诺。我的库没有实现我在这些示例中使用的所有功能,但可以使用它进行微调。关于承诺如何运作和不合理的基础规范是承诺/ A + 。它定义了 .then 方法的行为,并且非常易读,所以请务必仔细看看(不一定要直接)。

Q is the dominant promise implementation in node.js. I also have my own super light weight promises library Promise. My library doesn't implement all the features I've used in these examples, but it could be made to work with minor adaptation. The underpinning specification for how promises work and ineroperate is Promises/A+. It defines the behavior for a .then method and is pretty readable, so definitely give it a look at some point (not necessarily straight away).

承诺背后的想法是它们封装了一个异步值。这使得更容易推理如何将同步代码转换为异步代码,因为通常有很好的相似之处。作为对这些概念的介绍,我建议我就承诺和发电机或Domenic Denicola的谈话之一(例如承诺,承诺 Callbacks,Promises和Coroutines(哦,我的!)

The idea behind promises is that they encapsulate an asynchronous value. This makes it easier to reason about how to convert synchronous code into asynchronous code because there are usually nice parallels. As an introduction to these concepts I would recommend my talk on Promises and Generators or one of Domenic Denicola's talks (such as Promises, Promises or Callbacks, Promises, and Coroutines (oh my!)).

首先要确定的是,您是要并行提出请求,还是要按顺序提出请求。从问题我猜你想要并行完成它们。我也会假设你正在使用Q,这意味着你必须安装它:

The first thing to decide is whether you want to make your requests in parallel, or one at a time sequenctially. From the question I'm going to guess that you want to do them in parallel. I'm also going to assume you're using Q which means you'll have to install it with:

npm install q

并要求它在您使用它的每个文件的顶部:

and require it at the top of each file in which you use it:

var Q = require('q');

考虑用于打印报告的理想数据结构,我认为你有一系列品牌,其中包含一系列设备,这些设备的属性为 stage prod ,类似于:

Thinking about the ideal data structure to be using to print out that report, I think you'd have an array of brands, with an array of devices which would be objects with properties stage and prod, something like:

[
  {
      brand: 'A',
      devices: [
        {
          device: 'phone',
          stage: TAPE,
          prod: TAPE
        },
        {
          device: 'tablet',
          stage: TAPE,
          prod: TAPE
        }
        ...
      ]
  },
  {
      brand: 'B',
      devices: [
        {
          device: 'phone',
          stage: TAPE,
          prod: TAPE
        },
        {
          device: 'tablet',
          stage: TAPE,
          prod: TAPE
        }
        ...
      ]
  }
  ...
]

我会假设如果你有,那么你可以毫无困难地打印出所需的报告。

I'm going to assume that if you had that then you would have no trouble printing out the desired report.

让我们先看一下 getTape 功能。您是否希望它返回node.js流或包含整个下载文件的缓冲区/字符串?无论哪种方式,你都可以在图书馆的帮助下轻松找到它。如果您是node.js的新手,我建议将请求作为一个可以满足您期望的库。如果您感觉更自信,那么substack的 hyperquest 是一个小得多的库,可以说是更整洁,但它要求您处理重定向等内容手动,你可能不想进入。

Lets start by looking at the getTape function. Are you expecting it to return a node.js stream or a buffer/string containing the entire downloaded file? Either way, you're going to find it a lot easier with the help of a library. If you're new to node.js I'd recommend request as a library that just does what you'd expect. If you're feeling more confident, substack's hyperquest is a much smaller library and arguably neater, but it requires you to handle things like redirects manually, which you probably don't want to get in to.

流媒体方法很棘手。它可以完成,如果您的磁带长度为100 MB,则需要它,但承诺可能不是正确的方法。如果这是你实际遇到的问题,我很乐意更详细地研究这个问题。

The streaming approach is tricky. It can be done and will be needed if your tapes are 100s of MB long, but promises are then probably not the right way to go. I'm happy to look into this in more detail if it's an issue you actually have.

要使用请求创建一个缓冲HTTP请求的函数并返回一个承诺,这很简单。

To create a function that does a buffering HTTP request using request and returns a promise, it's fairly simple.

var Q = require('q')
var request = Q.denodeify(require('request'))

Q.denodeify 只是说话的捷径:接受这个通常需要回调的函数并给我一个承诺的函数。

Q.denodeify is just a shortcut for saying: "take this function that normally expects a callback and give me a function that takes a promise".

getTape 基于我们做的事情:

function getTape(env, brand, device) {
  var response = request({
    uri: 'http://example.com/' + env + '/' + brand + '/' + device,
    method: 'GET'
  })
  return response.then(function (res) {
    if (res.statusCode >= 300) {
      throw new Error('Server responded with status code ' + res.statusCode)
    } else {
      return res.body.toString() //assuming tapes are strings and not binary data
    }
  })
}

发生了什么事 request (通过 Q.denodeify )返回一个承诺。我们在该承诺上调用 .then(onFulfilled,onRejected)。这将返回一个新转换的promise。如果响应承诺被拒绝(相当于同步代码中的 throw ),那么转换后的承诺也是如此(因为我们没有附加 onRejected handler)。

What's happening there is that request (via Q.denodeify) is returning a promise. We're calling .then(onFulfilled, onRejected) on that promise. This returns a new transformed promise. If the response promise was rejected (equivalent to throw in synchronous code) then so is the transformed promise (because we didn't attach an onRejected handler).

如果你输入其中一个处理程序,转换后的promise将被拒绝。如果从其中一个处理程序返回一个值,那么转换后的promise将被履行(有时也称为已解决)并带有该值。然后我们可以在转换后的承诺结束时链接更多 .then 调用。

If you throw in one of the handlers, the transformed promise is rejected. If you return a value from one of the handlers then the transformed promise is "fulfilled" (also sometimes referred to as "resolved") with that value. We can then chain more .then calls on the end of our transformed promise.

我们将转换后的promise作为我们的函数的结果。

We return the transformed promise as the result of our function.

JavaScript有一个非常有用的函数叫做 .MAP 。它就像 .forEach 但返回一个转换后的数组。我将使用它来保持尽可能接近原始同步代码。

JavaScript has a really helpful function called .map. It's like .forEach but returns a transformed array. I'm going to use that to stay as close as possible to the original synchronous code.

var data = brands.map(function (brand) {
  var b = {brand: brand}
  b.devices = devices.map(function (device) {
    var d = {device: device}
    d.tapeS = getTape('stage',brand,device); // bad example...tapeS never set
    d.tapeP = getTape('prod' ,brand,device);
    return d
  })
})

现在我们有代码为我们提供数据我在开始时提出的结构,除了我们有 Promise< TAPE> 而不是 TAPE

Now we have code that gives us the data structure I proposed at the start, except we have Promise<TAPE> instead of TAPE.

Q有一个非常有用的方法叫做 Q.all 。它需要一系列的promise并等待它们全部完成,所以让我们将数据结构转换为一个promises数组,传递给Q.all。

Q has a really helpful method called Q.all. It takes an array of promises and waits for them all to complete, so lets turn our data structure into an array of promises to pass to Q.all.

一种方法这是最后,我们可以通过每个项目等待承诺解决。

One way to do this is at the end, we can go through each item and wait for the promises to resolve.

var updated = Q.all(data.map(function (brand) {
  return Q.all(brand.devices.map(function (device) {
    return Q.all([device.tapeS, device.tapeP])
      .spread(function (tapeS, tapeP) {
        //update the values with the returned promises
        device.tapeS = tapeS
        device.tapeP = tapeP
      })
  })
}))

//if you add a line that reads `updated = updated.thenResolve(data)`,
//updated would become a promise for the data structure (after being resolved)

updated.then(function () {
  // `data` structure now has no promises in it and is ready to be printed
})

另一个方法是我们去做,以便使请求代码被替换为:

Another aproach would be to do it as we go, so that the "making the requests" code gets replaced with:

var data = Q.all(brands.map(function (brand) {
  var b = {brand: brand}
  Q.all(devices.map(function (device) {
    var d = {device: device}
    var tapeSPromise = getTape('stage',brand,device);
    var tapePPromise = getTape('prod' ,brand,device);
    return Q.all([tapeSPromise, tapePPromise])
      .spread(function (tapeS, tapeP) { //now these are the actual tapes
        d.tapeS = tapeS
        d.tapeP = tapeP
        return d
      })
  }))
  .then(function (devices) {
    b.devices = devices
    return b
  })
}))

data.then(function (data) {
  // `data` structure now has no promises in it and is ready to be printed
})

还有一种方法是使用一个小的实用程序库做一个对象的递归深度解析。我还没有发布它,但这个效用函数(借用Kriskowal的作品)做了深刻的决心,允许你使用:

Still another approach would be to use a small utility library that does a recursive deep-resolve of an object. I haven't got round to publishing it, but this utility function (borrowed from work by Kriskowal) does a deep resolve, which would let you use:

var data = deep(brands.map(function (brand) {
  var b = {brand: brand}
  b.devices = devices.map(function (device) {
    var d = {device: device}
    d.tapeS = getTape('stage',brand,device); // bad example...tapeS never set
    d.tapeP = getTape('prod' ,brand,device);
    return d
  })
}))

data.then(function (data) {
  // `data` structure now has no promises in it and is ready to be printed
})

获得最终数据的承诺。

这篇关于Javascript Promise node.js?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆