我有一个像这样的递归函数
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 = ...。我们将解决所有这些问题!
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上面使用的帮助器
getPage
const getPage = async (page = 0, itemsPerPage = 5) => getProducts (page * itemsPerPage, itemsPerPage) .then (res => res.json ())
接下来,出于演示目的,我们介绍了一个伪getProducts函数,以及一个伪造的DB产品,每个产品只是一个数字。我们还使用它delay来模拟实际的网络延迟。
getProducts
DB
delay
在您的真实程序中,您只需要提供一个getProducts可以使用offset和limit输入查询产品的功能
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
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
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。这些问答可以帮助区分两者。
unfold