递归承诺不返回 [英] Recursive Promises Not Returning

查看:107
本文介绍了递归承诺不返回的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个像这样的递归函数

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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆