Gre*_*reg 14 javascript node.js promise
我是一个node.js新手,我试图理解如何以非阻塞的方式组织一些逻辑节点喜欢它.
我有一套环境['stage','prod'],以及另一组称为品牌['A','B','C']和一组设备['手机','平板电脑']的参数.
在节点的回调驱动的世界中,我有这样的:
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?
});
}
Run Code Online (Sandbox Code Playgroud)
我正在尝试为每个环境构建一个包含块的报告:
A:
Stage -- report
Prod -- report
B: ...
Run Code Online (Sandbox Code Playgroud)
我的问题是,因为这里的所有内容都是如此异步,特别是在调用节点的http.request的getTape中.如何在所有这些异步奇迹结束时序列化所有内容,以便按照我想要的顺序创建报告?
我听说过javascript Promises.这会有所帮助,即收集所有这些Promises的某种方式然后等待它们全部完成,然后获取它们收集的数据?
For*_*say 39
Q是node.js中的主要承诺实现.我也有自己的超轻量级承诺库Promise.我的库没有实现我在这些示例中使用的所有功能,但可以使用它进行微调.Promises/A +是承诺如何运作和不合理的基础规范.它定义了一个.then方法的行为并且非常易读,所以一定要看看它(不一定要直接).
承诺背后的想法是它们封装了一个异步值.这使得更容易推理如何将同步代码转换为异步代码,因为通常有很好的相似之处.作为这些概念的介绍,我会推荐我关于Promises and Generators的演讲或者Domenic Denicola的演讲(如Promises,Promises或Callbacks,Promises和Coroutines(哦,我的!)).
首先要决定的是,您是要并行提出请求,还是要按顺序提出请求.从问题我猜你想要并行完成它们.我也会假设你正在使用Q,这意味着你必须安装它:
npm install q
Run Code Online (Sandbox Code Playgroud)
并要求它在您使用它的每个文件的顶部:
var Q = require('q');
Run Code Online (Sandbox Code Playgroud)
关于理想的数据结构考虑使用打印出的报告是,我认为你有琳琅满目的品牌,与器件阵列,其将与属性的对象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
}
...
]
}
...
]
Run Code Online (Sandbox Code Playgroud)
我会假设如果你有那么你就可以毫不费力地打印出所需的报告.
让我们从查看getTape功能开始.您是否希望它返回node.js流或包含整个下载文件的缓冲区/字符串?无论哪种方式,你都可以在图书馆的帮助下轻松找到它.如果您是node.js的新手,我建议将请求作为一个可以满足您期望的库.如果你感觉更自信,那么substack的hyperquest是一个小得多的库,可以说是更整洁,但它要求你手动处理重定向等事情,你可能不想进入.
流媒体方法很棘手.它可以完成,如果您的磁带长度为100 MB,则需要它,但承诺可能不是正确的方法.如果这是你实际遇到的问题,我很乐意更详细地研究这个问题.
要创建一个使用请求缓冲HTTP 请求并返回promise 的函数,它非常简单.
var Q = require('q')
var request = Q.denodeify(require('request'))
Run Code Online (Sandbox Code Playgroud)
Q.denodeify 只是一个说法的捷径:"采取这个通常需要回调的函数,给我一个承诺的函数".
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
}
})
}
Run Code Online (Sandbox Code Playgroud)
正在发生的事情是request(通过Q.denodeify)正在返回一个承诺.我们正在呼吁.then(onFulfilled, onRejected)这一承诺.这将返回一个新转换的promise.如果响应promise被拒绝(相当于throw同步代码),那么转换后的promise也是如此(因为我们没有附加onRejected处理程序).
如果你输入其中一个处理程序,转换后的promise将被拒绝.如果从其中一个处理程序返回一个值,那么转换后的promise将被"履行"(有时也称为"已解决")并带有该值.然后,我们可以.then在转化的承诺结束时链接更多的调用.
作为函数的结果,我们返回转换后的promise.
JavaScript有一个非常有用的函数叫做.map.它就像.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
})
})
Run Code Online (Sandbox Code Playgroud)
现在我们有代码为我们提供了我在开始时提出的数据结构,除了我们Promise<TAPE>而不是TAPE.
Q有一个非常有用的方法叫做Q.all.它需要一组promises并等待它们全部完成,所以让我们将数据结构转换为一个promises数组,传递给Q.all.
一种方法是在最后,我们可以浏览每个项目并等待承诺解决.
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
})
Run Code Online (Sandbox Code Playgroud)
另一种方法是在我们去的时候这样做,以便"制作请求"代码被替换为:
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
})
Run Code Online (Sandbox Code Playgroud)
另一种方法是使用一个小的实用程序库来执行对象的递归深度解析.我还没有发布它,但是这个效用函数(从Kriskowal的工作中借用)做了很深的解决,可以让你使用:
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
})
Run Code Online (Sandbox Code Playgroud)
获得最终数据的承诺.
| 归档时间: |
|
| 查看次数: |
12745 次 |
| 最近记录: |