Javascript Promise node.js?
我是一个node.js新手,我试图理解如何以node喜欢的非阻塞方式组织一些逻辑 我有一套环境['stage','prod'],另一套参数叫做品牌['a','B','C'],还有一套设备['phone','tablet'] 在node的回调驱动世界中,我有以下几点: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,
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)
})