如何在 Promise 链中使用循环/递归?

3
例如,假设您想要将API返回的分页数据存储到数据库中。
let db;
let pageitems = 35
var offset = 0;

dbConnect //establish connection to database 
  .then( fetch(apiLink+?offset=2) 
  .then( res => res.json())
  .then( res => {
     var total = res.count
     return collection.insertMany(res.data, {ordered: false})
     // If offset is less than total, I want to increase offset and go back to the fetch-event.
  .catch( err => {
    if(err.code !== 11000){log(err)}
    else{log({completed: err.result.nInserted, duplicates: 
    err.result.result.writeErrors.length});}
  })
  .then(() => {
    connection.close();
  })

1
你的fetch只获取了pageitems(15)个项目?res.count是获取的记录数还是后端数据集的总大小? - trincot
2
你可以在 while 循环中使用 async/await。或者这里不适合使用 async/await? - Sim Dim
2
警告:您的代码立即调用了 fetch,而不是在 dbConnect 解析时调用。您必须在那里放置 () => fetch(... - trincot
2
是的,递归是正确的方法。但就像在非 Promise 的情况下一样,你首先需要一个可以递归调用的 函数 - Bergi
@bergi 在我看来,在这种情况下,循环更加简洁… - Jonas Wilms
1
@JonasWilms 当然可以,但是似乎 OP 首先尝试学习 ES6 Promise 链式编程。我发表的评论只是鼓励性质的,递归是正确的方法,应该尝试使用它。在后面采用 async / await 时,您当然可以使用循环。 - Bergi
2个回答

1
基本上,您需要将获取和插入包装到一个函数中,以便您可以多次调用它。请参见下面的示例,以说明我的观点...
let db;
let pageitems = 35
var offset = 0;


var db = dbConnect() //establish connection to database 

function fetch_and_insert(offset) {
    db
    .then(fetch(apiLink + "?" + offset))
    .then(res => res.json())
    .then(res => {
        var total = res.count
        collection.insertMany(res.data, { ordered: false })
        .catch(err => {
            if (err.code !== 11000) { log(err) }
            else {
                log({
                    completed: err.result.nInserted, duplicates: err.result.result.writeErrors.length
                });
            }
        })
        if (offset < total) return fetch_and_insert(offset + pageitems)
        return null;
    })
}

fetch_and_insert(offset)
.then(() => {
    connection.close();
})

1
您可以使用常规的循环:
 (async function() {
    const conn = await dbConnect;
    for(let offset = 0; true; offset++) { 
      const { data, count } = await (await fetch(`api?page=${offset}`)).json();
      // Exit if the page is empty
      if(count === 0) break;
      await collection.insertMany(data, { ordered: false });
    }
 })();

为了加快速度,您可以并行执行多个请求:
 const chunkSize = 10; // 10 in parallel
 for(let offset = 0; offset < chunkSize; offset++) {
   (async function() {
      const conn = await dbConnect;
      for(let offset2 = 0; true; offset2 += chunkSize) { 
        const { data, count } = await (await fetch(`api?page=${offset + offset2}`)).json();
        // Exit if the page is empty
        if(count === 0) break;
        await collection.insertMany(data, { ordered: false });
      }
   })();
 }

这看起来不错,我得更深入地了解一下await/async...(虽然我不想一次性调用它们,如果我这样做的话,API可能会讨厌我,而且这只是一个爬取操作...) - Himmators
1
@himmators 这肯定取决于具体的使用情况,很高兴能帮到你 :) - Jonas Wilms

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接