我有一个像这样的递归函数
function missingItemsPromise() {
return new Promise(resolve => {
if (missingItems == 0) {
console.log('resolves');
console.log(products);
return resolve();
} else {
page++;
url = getUrl(id, page);
http.get(url, function(xres) {
xres.setEncoding('utf8');
xres.on('data', function (xtraBody) {
console.log('calling');
var xtraJson = JSON.parse(xtraBody);
var xtraProducts = xtraJson['products'];
products = products.concat(xtraProducts);
productsLength = products.length;
missingItems = total - productsLength;
missingItemsPromise();
});
});
}
});
};
我正在使用它
getInitial.
then(missingItemsPromise).
then(() => {
console.log('hello');
});
我注意到您好永远不会返回,因为我怀疑我在递归调用上创建了多个promise,但是我不确定如何从中返回。
如何返回每个递归创建的Promise?
编辑:
function missingItemsPromise() {
return new Promise(resolve => {
if (missingItems == 0) {
console.log('resolves');
return resolve();
} else {
page++;
url = getUrl(id, page);
http.get(url, function(xres) {
xres.setEncoding('utf8');
xres.on('data', function (xtraBody) {
console.log('calling');
var xtraJson = JSON.parse(xtraBody);
var xtraProducts = xtraJson['products'];
products = products.concat(xtraProducts);
productsLength = products.length;
missingItems = total - productsLength;
missingItemsPromise();
resolve();
});
});
}
});
};
结果是
calling
hello <----notice here that it's already resolving once the first call resolve
is called
calling
calling
resolves
递归是一种功能性遗产,因此将其与功能性样式一起使用可产生最佳效果。这意味着编写接受和操作其输入(而不是依赖于外部状态)和返回值(而不是依赖于突变或副作用)的函数。
你的程序,而另一方面,调用函数不带参数,使用外部状态missingItems
,products
,productsLength
,total
,page
和用途类似的突变page++
和调动一样products = ...
,productsLength = ...
,missingItems = ...
。我们将解决所有这些问题!
我将通过这爆炸,希望它能使您走上正确的轨道。如果您被困在最后,我会链接一些其他答案,这些答案将更详细地说明此处使用的技术。
const getAllProducts = async (page = 0) =>
asyncUnfold
( async (next, done, [ res, nextPage ]) =>
res.products.length === 0
? done ()
: next ( res.products // value to add to output
, [ await getPage (nextPage), nextPage + 1 ] // next state
)
, [ await getPage (page), page + 1 ] // initial state
)
我们介绍getPage
上面使用的帮助器
const getPage = async (page = 0, itemsPerPage = 5) =>
getProducts (page * itemsPerPage, itemsPerPage)
.then (res => res.json ())
接下来,出于演示目的,我们介绍了一个伪getProducts
函数,以及一个伪造的DB
产品,每个产品只是一个数字。我们还使用它delay
来模拟实际的网络延迟。
在您的真实程序中,您只需要提供一个getProducts
可以使用offset
和limit
输入查询产品的功能
// fakes used for demonstration below
const getProducts = (offset = 0, limit = 1) =>
Promise.resolve
({ json: () =>
({ products: DB.slice (offset, offset + limit) })
})
.then (delay)
const delay = (x, ms = 250) =>
new Promise (r => setTimeout (r, ms, x))
const DB =
[ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20
, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30
, 31, 32, 33
]
下面我们演示运行程序。getAllProducts
是一个熟悉的异步函数,它返回结果的承诺。我们链接一个.then
电话,以便我们可以在控制台中看到所有产品页面的输出
getAllProducts () .then (console.log, console.error)
// ~2 seconds later
// [ [ 1, 2, 3, 4, 5 ]
// , [ 6, 7, 8, 9, 10 ]
// , [ 11, 12, 13, 14, 15 ]
// , [ 16, 17, 18, 19, 20 ]
// , [ 21, 22, 23, 24, 25 ]
// , [ 26, 27, 28, 29, 30 ]
// , [ 31, 32, 33 ]
// ]
不用按页面分组产品,如果我们可以将所有产品归为一个数组,那将是一个很好的选择。我们可以getAllProducts
稍作修改以实现此目的
const concat = (xs, ys) =>
xs .concat (ys)
const concatAll = (arrays) =>
arrays .reduce (concat, [])
const getAllProducts = async (page = 0) =>
asyncUnfold
( ... )
**.then (concatAll)**
getAllProducts () .then (console.log, console.error)
// ~2 seconds later
// [ 1, 2, 3, 4, 5, 6, 7, ..., 31, 32, 33 ]
最后,我们介绍 asyncUnfold
const asyncUnfold = async (f, initState) =>
f ( async (value, nextState) => [ value, ...await asyncUnfold (f, nextState) ]
, async () => []
, initState
)
完整程序演示
// dependencies -------------------------------------------------
const asyncUnfold = async (f, initState) =>
f ( async (value, nextState) => [ value, ...await asyncUnfold (f, nextState) ]
, async () => []
, initState
)
const concat = (xs, ys) =>
xs .concat (ys)
const concatAll = (arrays) =>
arrays .reduce (concat, [])
// fakes --------------------------------------------------------
const getProducts = (offset = 0, limit = 1) =>
Promise.resolve
({ json: () =>
({ products: DB.slice (offset, offset + limit) })
})
.then (delay)
const delay = (x, ms = 250) =>
new Promise (r => setTimeout (r, ms, x))
const DB =
[ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20
, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30
, 31, 32, 33
]
// actual program
const getAllProducts = async (page = 0) =>
asyncUnfold
( async (next, done, [ res, nextPage ]) =>
res.products.length === 0
? done ()
: next ( res.products
, [ await getPage (nextPage), nextPage + 1 ]
)
, [ await getPage (page), page + 1 ]
)
.then (concatAll)
const getPage = async (page = 0, itemsPerPage = 5) =>
getProducts (page * itemsPerPage, itemsPerPage)
.then (res => res.json ())
// demo ---------------------------------------------------------
getAllProducts ()
.then (console.log, console.error)
// ~2 seconds later
// [ 1, 2, 3, ..., 31, 32, 33 ]
我已经回答的有关递归和诺言的其他问题
异步和递归是独立的概念。如果您正在苦苦挣扎asyncUnfold
,首先了解它的同步副本可能会有所帮助unfold
。这些问答可以帮助区分两者。
问题内容: 我想遍历HTML 5文件系统中的所有文件,并在迭代完成后开始一些事件。由于这是异步+承诺,我很难尝试掌握其工作方式。 我正在使用angularJS,并创建了一个服务来封装html 5文件系统特定的功能。 这是递归函数: 理想情况下,我想这样调用该函数,并让其返回一个承诺,一旦遍历所有文件,该承诺便会执行。 有什么技巧/想法可以实现吗? 一个想法是拥有一个诺言数组,并为每个文件/目录向该
问题内容: 我有一个整数id的数组,例如 并且我需要为每个ID执行异步远程调用。每个调用都是一个使用$ resource执行的WebAPI请求,并显示为Promise。 我需要创建一个接受这些ID数组的函数,然后初始化递归承诺链。该链应导致对每个ID依次进行webapi调用。这些调用不应并行,而应链接在一起。 有问题的函数返回自己一个“主要”的承诺,该承诺应根据异步Web调用的结果来解决或拒绝。也
问题内容: 关于这两个重要来源:NZakas- 承诺链中的归还承诺 和MDN承诺,我想提出以下问题: 每次我们从承诺履行处理程序返回值时,该值如何传递给从同一处理程序返回的新承诺? 例如, 在这个例子中,是一个承诺。也是来自履行处理程序的承诺。但是。取而代之的是神奇地解决(如何?),然后将该值传递给的实现处理程序。即使是这里的句子也令人困惑。 您能给我解释一下这到底是怎么回事吗?我对这个概念感到困
问题内容: 我正在尝试使用递归调用从redis中获取数据,并在成员返回null时停止并返回。 所以我的数据是这样添加的: 最终数据应如下所示: 这是我正在弄乱的代码(从不同来源将它们拼凑在一起),但是我不知道自己在做什么。不知道这段代码是否有用,我可能会偏离正轨。 我可以看一下示例,从理论上看它应该如何工作,但是我无法确定它如何与q实现一起工作。任何帮助将不胜感激。 问题答案: 兑现承诺时,尽量做
问题内容: 我刚刚开始尝试在Node.js应用程序上实现Promises。现在,我正在检查用户和密码是否存在,然后使用mongodb来查找未找到用户的用户和密码,它会设置promise.reject(),但是它返回的promise太早了,它仍然处于未决状态。如果有人可以帮助或给我有关如何重构的想法,将不胜感激。 https://gist.github.com/joshbedo/8957056 问题
问题内容: 我正在使用promis模块从请求模块返回我的json数据,但是每次运行它时,它都会为我提供此信息。 我无法正常工作,有人知道这个问题吗?这是我的代码: 问题答案: 许诺是充当未来价值的占位符的对象。您的函数返回该Promise对象。通过将处理程序附加到promise,您可以在promise中获得未来的价值: 这是异步代码,因此,仅能通过处理程序来获得承诺的价值。 修改清单: 在返回的p