问题描述
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 undefinedTypeError: 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?
推荐答案
我一直在使用 Amorphisms 或unfold
在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
每次请求的时间为250ms
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 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') =>
asyncUnfoldObject
( async (next, done, stuff) =>
, ...
)
getAllStuff ()
.then (console.log, console.error)
// { a: 1, next: '/3', b: 2, c: 3, d: 4, e: 5 }
但是,您会说-具有asyncUnfoldArray
和asyncUnfoldObject
之类的名称的功能是完全不可接受的,我会同意.通过提供类型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') =>
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缺乏功能性语言,因为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 }
最后,如果您对触摸本机Array
或Object
上的属性感到困惑,则可以跳过该操作,而直接在其上传递通用描述符
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 }
// ]
这篇关于带有promise的递归调用异步函数将获得可能的未处理的承诺拒绝的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!