Promise with recursion

前端 未结 4 1422
鱼传尺愫
鱼传尺愫 2021-01-06 06:35

I\'ve taken a look at a few questions on recursion in promises and am confused on how to implement them properly:

  • Recursive Promise in javascript
  • Angu
4条回答
  •  攒了一身酷
    2021-01-06 07:10

    it would be better if you made i a parameter of the function instead of relying upon external state

    const countToTen = (i = 0) =>
      new Promise ((resolve, _) =>
        i < 10
          ? (console.log (i), resolve (countToTen (i + 1)))
          : resolve (i))
          
          
    countToTen () .then (console.log, console.error)
    // 0 1 2 3 4 5 6 7 8 9 10

    And even better if you made 10 a parameter too

    const countTo = (to, from = 0) =>
      new Promise ((resolve, _) =>
        from < to
          ? (console.log (from), resolve (countTo (to, from + 1)))
          : resolve (from))
    
    countTo (7, 2) .then (console.log, console.error)
    // 2 3 4 5 6 7

    A more generic approach is a reverse fold - or unfold

    const unfold = (f, init) =>
      f ( (x, acc) => [ x, ...unfold (f, acc) ]
        , () => []
        , init
        )
    
    const countTo = (to, from = 0) =>
      unfold
        ( (next, done, acc) =>
            acc <= to
              ? next (acc, acc + 1)
              : done ()
        , from
        )
    
    console.log (countTo (10))
    // [ 0, 1, 2, 3, 4, 5, 6,  7, 8, 9, 10 ]
    
    console.log (countTo (7, 2))
    // [ 2, 3, 4, 5, 6, 7 ]

    But you want a asynchronous unfold, asyncUnfold. Now the user-supplied function f can be async and we get a Promise of all collected values

    const asyncUnfold = async (f, init) =>
      f ( async (x, acc) => [ x, ...await asyncUnfold (f, acc) ]
        , async () => []
        , init
        )
    
    const delay = (x, ms = 50) =>
      new Promise (r => setTimeout (r, ms, x))
    
    const countTo = (to, from = 0) =>
      asyncUnfold
        ( async (next, done, acc) =>
            acc <= to
              ? next (await delay (acc), await delay (acc + 1))
              : done ()
        , from
        )
    
    countTo (10) .then (console.log, console.error)
    // [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]
    
    countTo (7, 2) .then (console.log, console.error)
    // [ 2, 3, 4, 5, 6, 7 ]

    Here's a more practical example where we have a database of records and we wish to perform a recursive look-up, or something...

    • db.getChildren accepts a node id and returns only the node's immediate children

    • traverse accepts a node id and it recursively fetches all descendant children (in depth-first order)

    const data =
      { 0 : [ 1, 2, 3 ]
      , 1 : [ 11, 12, 13 ]
      , 2 : [ 21, 22, 23 ]
      , 3 : [ 31, 32, 33 ]
      , 11 : [ 111, 112, 113 ]
      , 33 : [ 333 ]
      , 333 : [ 3333 ]
      }
    
    const db =
      { getChildren : (id) =>
          delay (data [id] || [])
      }
    
    const Empty =
      Symbol ()
    
    const traverse = (id) =>
      asyncUnfold
        ( async (next, done, [ id = Empty, ...rest ]) =>
            id === Empty
              ? done ()
              : next (id, [ ...await db.getChildren (id), ...rest ])
        , [ id ]
        )
    
    traverse (0) .then (console.log, console.error)
    // [ 0, 1, 11, 111, 112, 113, 12, 13, 2, 21, 22, 23, 3, 31, 32, 33, 333, 3333 ]

提交回复
热议问题