Javascript Promise node.js?

Javascript Promise node.js?,javascript,node.js,promise,Javascript,Node.js,Promise,我是一个node.js新手,我试图理解如何以node喜欢的非阻塞方式组织一些逻辑 我有一套环境['stage','prod'],另一套参数叫做品牌['a','B','C'],还有一套设备['phone','tablet'] 在node的回调驱动世界中,我有以下几点: brands.forEach( function(brand) { devices.forEach( function(device) { var tapeS = getTape('stage',brand,

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

我有一套环境['stage','prod'],另一套参数叫做品牌['a','B','C'],还有一套设备['phone','tablet']

在node的回调驱动世界中,我有以下几点:

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?
   });
}
我正在尝试为每个环境构建一个包含块的报告:

A:
    Stage -- report
    Prod  -- report 
B:    ...
我的问题是,因为这里的一切都是异步的,特别是在调用节点http.request的getTape内部。我如何在所有这些异步惊奇结束时序列化所有内容,以便按照我想要的顺序创建报告


我听说了一些关于javascript的承诺。这会有帮助吗?比如,用某种方法收集所有这些承诺,然后等待它们全部完成,然后获取它们收集的数据?

我对node.js也比较陌生,最近我发现了一些库,它们在以各种方式组织异步回调方面特别有效。然而,到目前为止我最喜欢的是。它有一些有用的模式,但我发现最有用的模式是async.series、async.parallel、async.瀑布。第一个是async.series,它只是按线性顺序执行异步函数:

async.series([
function(callback){
    // do some stuff ...
    callback(null, 'one');
},
function(callback){
    // do some more stuff ...
    callback(null, 'two');
}
],
// optional callback
function(err, results){
    // results is now equal to ['one', 'two']
});
第二个是async.parallel,它只是同时执行函数:

async.parallel([
function(callback){
    setTimeout(function(){
        callback(null, 'one');
    }, 200);
},
function(callback){
    setTimeout(function(){
        callback(null, 'two');
    }, 100);
}
],
// optional callback
function(err, results){
    // the results array will equal ['one','two'] even though
    // the second function had a shorter timeout.
});
最后一个也是我最喜欢的,与前面提到的async.series类似,但它也将上一个函数的结果传递给下一个函数:

async.waterfall([
function(callback){
    callback(null, 'one', 'two');
},
function(arg1, arg2, callback){
    callback(null, 'three');
},
function(arg1, callback){
    // arg1 now equals 'three'
    callback(null, 'done');
}
], function (err, result) {
    // result now equals 'done'    
});
那是我的作品。在我看来,这只是格式化node疯狂的非阻塞架构的最简单方法。如果你需要更多的帮助,给我一个下午。我知道node.js在更大、更复杂的代码库中会变得多么令人望而生畏


干杯。

承诺的另一种选择是使用模块:


如果你对使用承诺感兴趣,你可以看看我的图书馆。它模仿了许多函数的异步API,并且还具有一个“collect”函数,您刚才简要提到了这个函数

请注意,到目前为止,famility.parallel只接受数组,而不接受散列。这仍有待实现。

是node.js中主要的promise实现。我也有自己的超轻量图书馆。我的库并没有实现我在这些示例中使用的所有特性,但它可以进行轻微的修改。承诺如何工作和运作的基础规范是。它定义了
.then
方法的行为,并且可读性很好,所以一定要在某一点上看一下(不一定要马上看)

承诺背后的思想是它们封装了一个异步值。这使得如何将同步代码转换为异步代码变得更容易,因为通常存在很好的并行性。作为对这些概念的介绍,我推荐我的演讲或Domenic Denicola的演讲之一(如or)

首先要决定的是,您是想并行地提出请求,还是一次提出一个请求。从这个问题上,我猜你想同时做这些事情。我还假设您使用的是Q,这意味着您必须使用以下设备安装:

npm install q
并要求在使用它的每个文件的顶部使用它:

var Q = require('q');
考虑到打印该报告所使用的理想数据结构,我认为您将拥有一系列品牌,以及一系列设备,这些设备将是具有属性
stage
prod
的对象,类似于:

[
  {
      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
        }
        ...
      ]
  }
  ...
]
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
    }
  })
}
我将假设,如果你有,那么你将不会有麻烦打印出所需的报告

承诺的HTTP请求 让我们从查看
getTape
函数开始。您是否希望它返回包含整个下载文件的node.js流或缓冲区/字符串?不管怎样,在图书馆的帮助下,你会发现这要容易得多。如果您是node.js的新手,我建议您使用node.js作为一个库,它可以满足您的期望。如果你觉得更有信心,substack是一个更小的库,可以说更整洁,但它需要你手动处理重定向之类的事情,而你可能不想进入其中

流媒体(困难) 流媒体方法很棘手。如果您的磁带长度为100MB,那么这是可以做到的,而且是需要的,但承诺可能不是正确的方式。如果你真的有问题,我很乐意更详细地研究这个问题

请求缓冲(简单) 要创建一个使用缓存HTTP请求并返回承诺的函数,非常简单

var Q = require('q')
var request = Q.denodeify(require('request'))
Q.denodeify
只是一个快捷方式,表示“接受这个通常需要回调的函数,并给我一个接受承诺的函数”

要编写
getTape
,基于此,我们可以执行以下操作:

[
  {
      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
        }
        ...
      ]
  }
  ...
]
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
    }
  })
}
发生的事情是,
请求
(通过
Q.denodeify
)正在返回一个承诺。我们打电话给
。然后(履行承诺,拒绝承诺)
。这将返回一个新的转换承诺。如果响应承诺被拒绝(相当于同步代码中的
throw
),那么转换后的承诺也被拒绝(因为我们没有附加
onRejected
处理程序)

如果插入一个处理程序,则转换后的承诺将被拒绝。如果您从其中一个处理程序返回一个值,那么转换后的承诺将用该值“实现”(有时也称为“已解析”)。然后,我们可以链接更多的
。然后在转换后的承诺结束时调用

作为我们功能的结果,我们返回转换后的承诺

提出请求 JavaScript有一个非常有用的函数,名为。它类似于
.forEach
,但返回一个经过转换的数组。我将使用它尽可能接近原始同步代码

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
  })
})
现在我们有了代码,它给出了我在开始时提出的数据结构,e
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
})
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
})
var queue = require('queue-async')

var q = queue()

brands.forEach(function(brand){
    brand.devices.forEach(function(device){
        q.defer(getTape.bind(null, 'stage', brand, device))
        q.defer(getTape.bind(null, 'prod', brand, device))
    })
})

q.awaitAll(function(error, results){
    // use result pairs here
    console.log(results)
})