带有承诺的递归调用异步函数获得可能的未处理承诺拒绝 [英] Recursion call async func with promises gets Possible Unhandled Promise Rejection

查看:17
本文介绍了带有承诺的递归调用异步函数获得可能的未处理承诺拒绝的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

const PAGESIZE = 1000;
const DEFAULTLINK = `${URL}/stuff?pageSize=${PAGESIZE}&apiKey=${APIKEY}`;

export const getAllStuff = (initialLink = DEFAULTLINK) => {
  let allStuff = {};
  return getSuffPage(initialLink)
    .then(stuff => {
      allStuff = stuff;
      if (stuff.next) {
        return getAllStuff(stuff.next)
          .then(nextStuff => {
            allStuff = Object.assign({}, stuff, nextStuff);
            return allStuff;
          });
      } else {
        return allStuff;
      }
    });
};

const getSuffPage = nextPageLink => {
  fetch(nextPageLink).then(res => {
    return res.json();
  });
};

调用 getAllStuff 会抛出:

Calling getAllStuff throws:

可能的未处理承诺拒绝(id:0):TypeError:无法读取未定义的属性then"TypeError:无法读取未定义的属性then"在getAllStuff

Possible Unhandled Promise Rejection (id: 0): TypeError: Cannot read property 'then' of undefined TypeError: Cannot read property 'then' of undefined at getAllStuff

我认为这通常是因为我没有从承诺中返回或其他什么,但我没有在哪里?

I think it is usually because I do not return from a promise then or something but where don't I?

推荐答案

我一直在使用 anamorphismsunfold 在 JavaScript 最近 我想我可以用您的程序作为学习它们的上下文

I've been working with anamorphisms or unfold in JavaScript lately and I thought I might share them with you using your program as a context to learn them in

const getAllStuff = async (initUrl = '/0') =>
  asyncUnfold
    ( async (next, done, stuff) =>
        stuff.next
          ? next (stuff, await get (stuff.next))
          : done (stuff)
    , await get (initUrl)
    )

const get = async (url = '') =>
  fetch (url) .then (res => res.json ())

为了证明这是可行的,我们引入了一个假的 fetch 和数据库 DB,每个请求的假 delay 为 250 毫秒

To demonstrate that this works, we introduce a fake fetch and database DB with a fake delay of 250ms per request

const fetch = (url = '') =>
  Promise.resolve ({ json: () => DB[url] }) .then (delay)

const delay = (x, ms = 250) =>
  new Promise (r => setTimeout (r, ms, x))

const DB = 
  { '/0': { a: 1, next: '/1' }
  , '/1': { b: 2, next: '/2' }
  , '/2': { c: 3, d: 4, next: '/3' }
  , '/3': { e: 5 }
  }

现在我们像这样运行我们的程序

Now we just run our program like this

getAllStuff () .then (console.log, console.error)

// [ { a: 1, next: '/1' }
// , { b: 2, next: '/2' }
// , { c: 3, d: 4, next: '/3' }
// , { e: 5 }
// ]

最后,这里是 asyncUnfold

const asyncUnfold = async (f, init) =>
  f ( async (x, acc) => [ x, ...await asyncUnfold (f, acc) ]
    , async (x) => [ x ]
    , init
    )

程序演示1

const asyncUnfold = async (f, init) =>
  f ( async (x, acc) => [ x, ...await asyncUnfold (f, acc) ]
    , async (x) => [ x ]
    , init
    )

const getAllStuff = async (initUrl = '/0') =>
  asyncUnfold
    ( async (next, done, stuff) =>
        stuff.next
          ? next (stuff, await get (stuff.next))
          : done (stuff)
    , await get (initUrl)
    )

const get = async (url = '') =>
  fetch (url).then (res => res.json ())

const fetch = (url = '') =>
  Promise.resolve ({ json: () => DB[url] }) .then (delay)

const delay = (x, ms = 250) =>
  new Promise (r => setTimeout (r, ms, x))

const DB = 
  { '/0': { a: 1, next: '/1' }
  , '/1': { b: 2, next: '/2' }
  , '/2': { c: 3, d: 4, next: '/3' }
  , '/3': { e: 5 }
  }

getAllStuff () .then (console.log, console.error)

// [ { a: 1, next: '/1' }
// , { b: 2, next: '/2' }
// , { c: 3, d: 4, next: '/3' }
// , { e: 5 }
// ]

现在假设您想将结果折叠到单个对象中,我们可以使用 reduce 来实现 - 这更接近于您的原始程序所做的.注意 next 属性如何在发生键冲突时遵循最后一个值

Now say you wanted to collapse the result into a single object, we could do so with a reduce – this is closer to what your original program does. Note how the next property honors the last value when a key collision happens

getAllStuff ()
  .then (res => res.reduce ((x, y) => Object.assign (x, y), {}))
  .then (console.log, console.error)

// { a: 1, next: '/3', b: 2, c: 3, d: 4, e: 5 }

如果你很敏锐,你会发现 asyncUnfold 可以更改为直接输出我们的对象.我选择输出一个数组是因为展开结果的顺序通常很重要.如果您从 type 的角度考虑这个问题,每个可折叠类型的 fold 都有一个同构的 unfold.

If you're sharp, you'll see that asyncUnfold could be changed to output our object directly. I chose to output an array because the sequence of the unfold result is generally important. If you're thinking about this from a type perspective, each foldable type's fold has an isomorphic unfold.

下面我们将asyncUnfold重命名为asyncUnfoldArray,并引入asyncUnfoldObject.现在我们看到直接结果是可以在没有中间 reduce 步骤的情况下实现的

Below we rename asyncUnfold to asyncUnfoldArray and introduce asyncUnfoldObject. Now we see that the direct result is achievable without the intermediate reduce step

const asyncUnfold = async (f, init) =>
const asyncUnfoldArray = async (f, init) =>
  f ( async (x, acc) => [ x, ...await asyncUnfoldArray (f, acc) ]
    , async (x) => [ x ]
    , init
    )

const asyncUnfoldObject = async (f, init) =>
  f ( async (x, acc) => ({ ...x, ...await asyncUnfoldObject (f, acc) })
    , async (x) => x
    , init
    )

const getAllStuff = async (initUrl = '/0') =>
  asyncUnfold
  asyncUnfoldObject
    ( async (next, done, stuff) =>
    , ...
    )

getAllStuff ()
  .then (res => res.reduce ((x, y) => Object.assign (x, y), {}))
  .then (console.log, console.error)

// { a: 1, next: '/3', b: 2, c: 3, d: 4, e: 5 }

<小时>

但是你会说,拥有像 asyncUnfoldArrayasyncUnfoldObject 这样名称的函数是完全不可接受的,我会同意的.整个过程可以通过提供类型 t 作为参数来通用


But having functions with names like asyncUnfoldArray and asyncUnfoldObject is completely unacceptable, you'll say - and I'll agree. The entire process can be made generic by supplying a type t as an argument

const asyncUnfold = async (t, f, init) =>
  f ( async (x, acc) => t.concat (t.of (x), await asyncUnfold (t, f, acc))
    , async (x) => t.of (x)
    , init
    )

const getAllStuff = async (initUrl = '/0') =>
  asyncUnfoldObject
  asyncUnfold
    ( Object
    , ...
    , ...
    )

getAllStuff () .then (console.log, console.error)

// { a: 1, next: '/3', b: 2, c: 3, d: 4, e: 5 }

现在如果我们想构建一个数组,只需传递 Array 而不是 Object

Now if we want to build an array instead, just pass Array instead of Object

const getAllStuff = async (initUrl = '/0') =>
  asyncUnfold
    ( Array
    , ...
    , ...
    )

getAllStuff () .then (console.log, console.error)

// [ { a: 1, next: '/1' }
// , { b: 2, next: '/2' }
// , { c: 3, d: 4, next: '/3' }
// , { e: 5 }
// ]

当然,我们必须承认 JavaScript 在这一点上缺乏函数式语言,因为它甚至没有为自己的原生类型提供一致的接口.没关系,它们很容易添加!

Of course we have to concede JavaScript's deficiency of a functional language at this point, as it does not provide consistent interfaces for even its own native types. That's OK, they're pretty easy to add!

Array.of = x =>
  [ x ]

Array.concat = (x, y) =>
  [ ...x, ...y ]

Object.of = x =>
  Object (x)

Object.concat = (x, y) =>
  ({ ...x, ...y })

程序演示2

Array.of = x =>
  [ x ]
  
Array.concat = (x, y) =>
  [ ...x, ...y ]
  
Object.of = x =>
  Object (x)

Object.concat = (x, y) =>
  ({ ...x, ...y })

const asyncUnfold = async (t, f, init) =>
  f ( async (x, acc) => t.concat (t.of (x), await asyncUnfold (t, f, acc))
    , async (x) => t.of (x)
    , init
    )

const getAllStuff = async (initUrl = '/0') =>
  asyncUnfold
    ( Object // <-- change this to Array for for array result
    , async (next, done, stuff) =>
        stuff.next
          ? next (stuff, await get (stuff.next))
          : done (stuff)
    , await get (initUrl)
    )

const get = async (url = '') =>
  fetch (url).then (res => res.json ())

const fetch = (url = '') =>
  Promise.resolve ({ json: () => DB[url] }) .then (delay)

const delay = (x, ms = 250) =>
  new Promise (r => setTimeout (r, ms, x))

const DB = 
  { '/0': { a: 1, next: '/1' }
  , '/1': { b: 2, next: '/2' }
  , '/2': { c: 3, d: 4, next: '/3' }
  , '/3': { e: 5 }
  }

getAllStuff () .then (console.log, console.error)

// { a: 1, next: '/3', b: 2, c: 3, d: 4, e: 5 }

最后,如果您对触摸原生 ArrayObject 上的属性大惊小怪,您可以跳过它,直接传入通用描述符

Finally, if you're fussing about touching properties on the native Array or Object, you can skip that and instead pass a generic descriptor in directly

const getAllStuff = async (initUrl = '/0') =>
  asyncUnfold
    ( { of: x => [ x ], concat: (x, y) => [ ...x, ...y ] } 
    , ...
    )

getAllStuff () .then (console.log, console.error)

// [ { a: 1, next: '/1' }
// , { b: 2, next: '/2' }
// , { c: 3, d: 4, next: '/3' }
// , { e: 5 }
// ]

这篇关于带有承诺的递归调用异步函数获得可能的未处理承诺拒绝的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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