带有promises的递归调用async func得到可能的未处理的Promise Rejection

时间:2018-04-05 07:12:08

标签: asynchronous recursion fetch es6-promise

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抛出:

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

我认为这通常是因为我没有从承诺那里回来,或者某些东西,但不是我不在的地方?

1 个答案:

答案 0 :(得分:2)

我一直在JavaScript anamorphisms中使用latelyunfold,我想我可能会将您的程序作为上下文与他们分享

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毫秒

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 }
// ]

最后,这里是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属性如何在发生密钥冲突时遵循最后一个值

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可以更改为直接输出我们的对象。我选择输出一个数组,因为展开结果的顺序通常很重要。如果您从类型的角度考虑这一点,则每个可折叠类型的折叠都有一个同构展开

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

的情况下实现
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作为参数

,可以使整个过程成为通用的
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

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在此时缺乏函数式语言,因为它甚至不为其自己的本机类型提供一致的接口。没关系,他们很容易添加!

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 }
&#13;
&#13;
&#13;

最后,如果您对触及本机ArrayObject上的属性感到烦恼,可以跳过它,而不是直接传递通用描述符

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 }
// ]