我有一个像这样的递归函数
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永远不会返回,因为我怀疑我在递归调用中创建了多个承诺,但我不确定如何退出它。
如何返回每个递归创建的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
答案 0 :(得分:1)
递归是一种功能性遗产,因此将其与功能风格结合使用可获得最佳效果。这意味着编写接受和操作其输入的函数(而不是依赖于外部状态)并返回值(而不是依赖于突变或副作用)。
另一方面,您的程序调用不带参数的函数,使用外部状态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
输入查询产品
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
调用,以便我们可以在控制台中看到所有产品页面输出
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 ]
&#13;
我已回答有关递归和承诺的其他问题
异步和递归是不同的概念。如果您正在与asyncUnfold
进行斗争,那么首先了解其同步对应unfold
可能会有所帮助。这些Q&amp; A可能有助于区分这两者。
答案 1 :(得分:0)
你缺少else条件中的return语句
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;
return missingItemsPromise(); //this is the line that changes
});
});
}
});
};