递归承诺不返回 [英] Recursive Promises Not Returning
问题描述
我有一个像这样的递归函数
I have a recursive function like so
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');
});
我注意到hello永远不会返回,因为我怀疑我创造了不止一个承诺在一个递归调用,但我不确定如何退出它。
I'm noticing that the hello never returns because I suspect that I'm creating more than one promise on a recursive call but I"m unsure of how to return out of it.
我如何返回每个递归创建的承诺?
How can I return each recursively created 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
推荐答案
递归是一种功能性遗产,因此将其与功能风格一起使用产生最好的结果。这意味着编写接受和操作其输入的函数(而不是依赖于外部状态)并返回值(而不是依赖于突变或副作用)。
Recursion is a functional heritage and so using it with functional style yields the best results. That means writing functions that accept and operate on their inputs (rather than relying on external state) and return values (rather than relying on mutation or side effects).
您的程序,另一方面,调用没有参数的函数,使用外部状态 missingItems
,产品
, productsLength
,总计
,页
并使用 page ++
和重新分配,如 products = ...
, productsLength = ...
, missingItems = ...
。我们要解决所有这些问题!
Your program, on the other hand, calls functions without arguments, uses external state missingItems
, products
, productsLength
, total
, page
and uses mutations like page++
and reassignments like products = ...
, productsLength = ...
, missingItems = ...
. We're gonna fix all of this!
我只是要通过这个来爆炸,希望它能让你走上正轨。如果你被困在最后,我会链接一些其他答案,这些答案将更详细地解释这里使用的技术。
I'm just going to blast thru this and hope it sets you on the right track. If you're stuck at the end, I link some other answers which explain the techniques used here in greater detail.
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
我们上面使用的助手
We introduce the getPage
helper we used above
const getPage = async (page = 0, itemsPerPage = 5) =>
getProducts (page * itemsPerPage, itemsPerPage)
.then (res => res.json ())
接下来,为了本演示的目的,我们引入了假的 getProducts
函数和假的 DB
每个产品只是一个数字。我们还使用 delay
来模拟真实的网络延迟。
Next, for the purposes of this demo, we introduce a fake getProducts
function, and a fake DB
where each product is simply a number. We also use delay
to simulate real network delay.
在您的真实程序中,您只需提供一个 getProducts
可以使用 offset
和限制
输入<查询产品的函数/ p>
In your real program, you just need to provide a getProducts
function that can query products using offset
and limit
inputs
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
是一个熟悉的异步函数,它返回其结果的Promise。我们链接 .then
调用,以便我们可以在控制台中看到所有产品页面输出
Below we demonstrate running the program. getAllProducts
is a familiar async function which returns a Promise of its result. We chain a .then
call so we can see all of the product pages output in the console
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
以实现此目的
Instead of grouping products by page, it'd be nice if we could return all the products in a single array. We can modify getAllProducts
slightly to achieve this
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 ]
我已回答有关递归和承诺的其他问题
Other questions I've answered about recursion and promises
- Recursion call async func with promises gets Possible Unhandled Promise Rejection
- Recursive JS function with setTimeout
- Promise with recursion
异步和递归是单独的概念。如果你正在努力解决 asyncUnfold
,那么首先要了解它的同步对应展开
可能会有所帮助。这些Q& A可能有助于区分这两者。
Asynchrony and recursion are separate concepts. If you're struggling with asyncUnfold
, it might help to first understand its synchronous counterpart unfold
. These Q&A's may help distinguish the two.
- Functional way to create an array of numbers
- Loop until... with Ramda
- Node.js recursively list full path of files
这篇关于递归承诺不返回的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!