递归JavaScript承诺(自动分页API)



我有一个分页的API,我需要自动获取每一页的结果。

我构建了以下递归承诺链,它实际上为我提供了所需的输出。我已经将传统的决心重命名为outer,试图让我的头脑更加清醒,但我仍然对如何将outer函数传递到嵌套中感到困惑。那么,是神奇的酱汁让它发挥作用吗?

我以为我很了解Promises,但显然不是!

protected execute<T>(httpOptions): Promise<AxiosResponse | T> {
const reqObject: HttpConfig = {
...httpOptions,
baseURL: this.config.baseUri,
headers: { Authorization: `Bearer ${this.config.apiKey}` },
};
return new Promise((outer, reject) => {
axios
.request<T>(reqObject)
.then((res) => {
if (res.headers.link) {
const pagingObject: PagingObject = this.getPagingObject(res);
this.pagedListResponses = this.pagedListResponses.concat(res.data);
if (pagingObject.last) {
this.execute<T>({
url: pagingObject.next,
}).then(outer);
} else {
outer(this.pagedListResponses as any);
}
} else {
outer("No paging header found");
}
})
.catch((err) => reject(err));
});
}

TL:DR

当你通过递归添加越来越多的Promise时,它们就像洋葱皮一样堆积起来。当每一个都被解析并调用.then()时,Promises将从内到外删除,直到一个都没有。

承诺链接

Resolve处理程序中创建Promise对象时在另一个承诺中,两个承诺都是按照由内而外的顺序解决的。此外,当链接通过Promise实例方法(.then().catch()、.always()等(,链接方法具有优先级在最外层Promise对象解析之前执行。

也许在这里解释得更好。

您的代码在外部构造的Promise对象的Resolve处理程序中创建Axios Promise。这个AxiosPromise的.then()将在外部Promise最终结算之前执行。之后,结果通过外部Promise对象传递,而不进行修改或处理。基本上是一个无操作

这就是为什么包装器Promise(显式构造反模式(是不必要的,也是令人沮丧的——这是对时间和资源的浪费,在这个代码中没有任何好处。

随着递归的混合,Promise对象不断堆积

因此(目前(从.execute()方法返回了对外部Promise对象的引用。但何时/如何解决(解决或拒绝(?

  1. Axios Promise通过AJAX结果解决
    • 如果这是一个递归调用,那么这就是一个内部Promise(即使是用.then(outer)创建的,在这种情况下更容易混淆(
  2. 使用AJAX结果调用最外层的.then()(或有理由拒绝(
  3. 使用结果更新实例变量pagedListResponses
  4. 如果res.headers.link == true && pageObject.last == true
    • 重复&从1<-开始待定有未结算的承诺
  5. 否则如果res.headers.link == true && pageObject.last == false
    • pageListResponses<-完全解决
  6. 否则,如果res.headers.link == false && pageObject.last == false
    • 用"未找到寻呼标头"<-完全解决
  7. 然后用pageListResponses调用附加到对execute()的初始调用的then()方法
    • 例如。this.execute({...}).then(pageList=>doSomethingWithPageOfResults());

因此,使用.then()中游的链接允许我们在返回最终解决了Promise的结果。

在递归代码中:

this.execute<T>({
url: pagingObject.next,
}).then(outer);

这里的.then()调用只是将一个新的内部Promise添加到链中,正如您所意识到的,这与编写完全相同

.then(result=>outer(result));参考

异步/等待

最后,建议使用Async/Await。强烈建议使用Async/Await重写Promise代码,用于复杂的场景(或者说任何(。尽管仍然是异步的,但这使得阅读和合理化代码序列变得更加容易。

您的代码被重写以利用Async/Await:

protected async execute<T>(httpOptions): Promise<T> {
const reqObject = {
...httpOptions,
baseURL: this.config.baseUri,
headers: {Authorization: `Bearer ${this.config.apiKey}`},
};
try {
const res: AxiosResponse<T> = await axios.request<T>(reqObject);
if (res) {
const pagingObject: boolean = this.getPagingObject(res);
this.pagedListResponses = this.pagedListResponses.concat(res.data);
if (pagingObject) {
return this.execute<T>({ url: 'https://jsonplaceholder.typicode.com/users/1/todos'});
} else {
return this.pagedListResponses as any;
}
} else {
return "No paging header found" as any;
}
} catch (err) {
throw new Error('Unable to complete request for data.');
}
}

这里async直接表示";该方法返回Promise";(按键入(。await关键字在代码中只使用一次以等待解决AJAX请求。该语句返回实际结果(按类型(,而不是Promise对象。网络错误会发生什么?注意此处try/catch的使用。catch块处理嵌套/链中的任何拒绝。您可能会注意到递归调用没有await关键字。在这种情况下没有必要,因为这是一个只传递其结果的Promise。(在这里添加await是可以的,但没有真正的好处(。

此代码在外部的使用方式相同:this.execute({...}).then(pageList=>doSomethingWithPageOfResults());

让我知道你可能还有什么差距。

当您调用this.execute时,它会返回一个Promise<AxiosResponse | T>

因此,通过调用.then(outer),递归execute函数的响应将用于解析外部promise。

它基本上与this.execute(...).then(response => outer(response))相同,如果这让它更清晰的话。

最新更新