gpt4 book ai didi

Javascript promise node.js?

转载 作者:IT老高 更新时间:2023-10-28 23:03:52 25 4
gpt4 key购买 nike

我是一个 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: ...

我的问题是,由于这里的所有内容都是异步的,尤其是在调用 Node 的 http.request 的 getTape 内部。如何在所有这些异步奇迹结束时序列化所有内容,以便我可以按照我想要的顺序创建报告?

我听说了一些关于 javascript Promises 的事情。这会有所帮助,即以某种方式收集所有这些 promise ,然后等待它们全部完成,然后获取它们收集的数据吗?

最佳答案

Q是 node.js 中主要的 promise 实现。我也有自己的超轻量 promise 库 Promise .我的库没有实现我在这些示例中使用的所有功能,但可以通过较小的调整使其工作。 Promise 如何工作和互操作的基础规范是 Promises/A+ .它定义了 .then 的行为方法并且可读性很强,所以一定要在某个时候看一下(不一定是马上)。

Promise 背后的想法是它们封装了一个异步值。这使得更容易推理如何将同步代码转换为异步代码,因为通常有很好的并行性。作为对这些概念的介绍,我推荐我在 Promises and Generators 上的演讲。或 Domenic Denicola 的演讲之一(例如 Promises, PromisesCallbacks, Promises, and Coroutines (oh my!))。

要决定的第一件事是您是要并行发出请求,还是按顺序一次发出一个请求。从这个问题我猜你想并行做它们。我还将假设您正在使用 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
}
...
]
}
...
]

我将假设,如果您拥有它,那么您就可以轻松打印出所需的报告。

promise 的 HTTP 请求

让我们先看看 getTape功能。您是否希望它返回包含整个下载文件的 node.js 流或缓冲区/字符串?无论哪种方式,在图书馆的帮助下,您都会发现它更容易。如果您是 node.js 的新手,我建议您使用 request作为一个可以满足您期望的库。如果你感觉更自信,substack 的 hyperquest是一个小得多的库,可以说更整洁,但它需要您手动处理重定向之类的事情,而您可能不想这样做。

流媒体(困难)

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

请求缓冲(简单)

使用 request 创建一个缓冲 HTTP 请求的函数并返回一个 promise ,这相当简单。
var Q = require('q')
var request = Q.denodeify(require('request'))
Q.denodeify只是说:“使用这个通常需要回调的函数并给我一个接受 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 )正在返回一个 promise 。我们正在调用 .then(onFulfilled, onRejected)在那个 promise 上。这将返回一个新的转换后的 promise 。如果响应 promise 被拒绝(相当于同步代码中的 throw),那么转换后的 promise 也会被拒绝(因为我们没有附加一个 onRejected 处理程序)。

如果您投入其中一个处理程序,则转换后的 Promise 将被拒绝。如果您从其中一个处理程序返回一个值,则转换后的 promise 将使用该值“实现”(有时也称为“已解决”)。然后我们可以链接更多 .then呼唤我们转变后的 promise 的结束。

我们返回转换后的 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
})
})

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

等待请求

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

一种方法是在最后,我们可以遍历每个项目并等待 promise 解决。
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
})

另一种方法是边走边做,以便“发出请求”代码被替换为:
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
})

还有一种方法是使用一个小的实用程序库来递归地解析对象。我还没有时间发布它,但是 this utility function (从 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
})

获得对最终数据的 promise 。

关于Javascript promise node.js?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16845624/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com