Javascript 在解析承诺之前,有没有一种方法可以填充JSON对象

Javascript 在解析承诺之前,有没有一种方法可以填充JSON对象,javascript,json,for-loop,promise,Javascript,Json,For Loop,Promise,代码首先从数据库获取所有URL。 在parseText中,我试图解析所有的ur,并将它们放在一个Json对象中供以后参考 我试着用async/await运行for循环,但没有得到预期的结果 let parseText = function(dbresult) { return new Promise((resolve, reject) => { let textObj = {} for(i=0; i < dbresult; i++) { Mer

代码首先从数据库获取所有URL。 在parseText中,我试图解析所有的ur,并将它们放在一个Json对象中供以后参考

我试着用async/await运行for循环,但没有得到预期的结果

let parseText = function(dbresult) {
    return new Promise((resolve, reject) => {
    let textObj = {}

    for(i=0; i < dbresult; i++) {
       Mercury.parse(dbresult[i].url, {headers: {Cookie: 'name=Bs', 'User-Agent': 'Mozilla/5.0 (Linux; Android 6.0.1; Nexus 5X Build/MMB29P) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.96 Mobile Safari/537.36 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)', },})
       .then((result) => {
           textObj[dbresult[i].id] = result.excerpt;
      });
    }
    resolve(textObj);
  })
}


fetchLinks.then(function(result) {
    return parseText(result);
  }).then(function(result) {
  console.log(result); //gives back {}
  //do something with json object in next promise
  return writeTextToDb(result); //not written yet
})
let parseText=function(dbresult){
返回新承诺((解决、拒绝)=>{
设textObj={}
对于(i=0;i{
textObj[dbresult[i].id]=result.摘录;
});
}
解决(textObj);
})
}
fetchLinks.then(函数(结果){
返回解析文本(结果);
}).然后(函数(结果){
console.log(result);//返回{}
//在下一个承诺中使用json对象
返回writeTextToDb(结果);//尚未写入
})

所需的输出应该类似于{1234:{text:some parsed text}},但我一直得到的只是一个空对象

在代码中有很多东西需要处理,所以让我们一步一步地进行:

  • dbresult
    从您使用的
    dbresult[i]
    判断,似乎是一个数组,但您也有一个
    i
    条件,表示它是一个整数。我要告诉你你的意思是
    I
  • 您正在使用
    newpromise(…)
    的情况下,您已经在处理承诺了。除非没有其他选择,否则永远不要使用此模式,并始终尝试链接
    。然后
    调用并返回它们的结果(这也是承诺)
  • 您似乎没有理解传递给
    的回调。那么
    将始终在其余代码运行之后异步运行。这就是对象变为空的原因:在任何请求有时间完成之前调用
    resolve
    函数
现在,循环和承诺不能很好地结合,但是有办法处理它们。你需要理解的是,通过循环,你想要的是连锁承诺。以这种方式链接承诺的方式主要有两种:命令式方式和功能性方式

我将重点讨论
parseText
函数,并省略不相关的细节。对于一个完全必要的解决方案,您可以这样做:

function parseText (dbresult) {
    // although the contents of the object change, the object doesn't,
    // so we can just use const here
    const textObj = {};

    // initialize this variable to a dummy promise
    let promise = Promise.resolve();

    // dbresult is an array, it's clearer to iterate this way
    for (const result of dbresult) {
       // after the current promise finishes, chain a .then and replace
       // it with the returned promise.  That will make every new iteration
       // append a then after the last one.
       promise = promise
         .then(() => Mercury.parse(result.url, {...}))
         .then((response) => (textObj[result.id] = response.excerpt));
    }

    // in the end, the promise stored in the promise variable will resolve
    // after all of that has already happened.  We just need to return the
    // object we want to return and that's it.
    return promise.then(() => textObj);
}
我希望这些评论能有所帮助。同样,在一个循环中与承诺合作很糟糕

不过,有两种方法可以更轻松地实现这一点!两者都使用数组的函数方法。第一个是最简单的,我推荐它,除非阵列非常大。它利用了
.map
承诺。所有
,两个强大的盟友:

function parseText (dbresult) {
    const textObj = {};

    // create an array with all the promises
    const promises = dbresult.map(result => Mercury.parse(result.url, {...})
        .then((response) => (textObj[result.id] = response.excerpt)))
    );

    // await for all of them, then return our desired object
    return Promise.all(promises).then(() => textObj);
}
注意:
bluebird
用户可以通过使用
Promise.map
并传递
并发性
值来实现这一点。在我看来,这实际上是最好的解决方案,但我想在这里坚持香草口味

不过,此解决方案的主要问题是,所有请求都将立即启动。这可能意味着,对于非常大的阵列,一些请求只是在队列中等待,或者您耗尽了进程的套接字限制,具体取决于实现。无论如何,这并不理想,但在大多数情况下都会奏效

另一个功能解决方案是使用
.reduce
而不是
来复制命令式解决方案。。。循环,并在答案的末尾实现,更像是一种好奇心,而不是其他任何东西,因为我认为它有点太“聪明的代码”

在我看来,解决这个问题的最好方法就是使用
async/await
并完全忘记承诺。在这种情况下,您可以正常地编写循环,并在适当的地方简单地放置wait:

async function parseText (dbresult) {
    const textObj = {};

    for (const result of dbresult) {
        // here just await the request, then do whatever with it
        const response = await Mercury.parse(result.url, {...}))
        textObj[result.id] = response.excerpt;
    }

    // thanks to await, here we already have the result we want
    return textObj;
}
就这样,就这么简单


和现在我认为是“聪明”的解决方案,只使用<代码> .Reals >:

function parseText (dbresult) {
    const textObj = {};
    return dbresult.reduce(
        (prom, result) => prom
            .then(() => Mercury.parse(result.url, {...}))
            .then((response) => (textObj[result.id] = response.excerpt)),
        Promise.resolve()
    ).then(() => textObj);
}
如果现在还不清楚它是干什么的,那是正常的。这与原始命令
的作用完全相同,然后
-链接一个命令,只需使用
.reduce
而不是手动
进行
循环


请注意,我个人不一定要这样做,因为我认为这有点太“聪明”,需要一些时间来进行心理分析。如果实现类似的功能(
,那么
-使用
.reduce
链接非常有用,即使有点混乱),请添加一条注释,解释为什么要这样做,它是什么,或者可以帮助其他开发人员一眼就理解ir。

您的代码中有很多东西需要处理,让我们一步一步来:

  • dbresult
    从您使用的
    dbresult[i]
    判断,似乎是一个数组,但您也有一个
    i
    条件,表示它是一个整数。我要告诉你你的意思是
    I
  • 您正在使用
    newpromise(…)
    的情况下,您已经在处理承诺了。除非没有其他选择,否则永远不要使用此模式,并始终尝试链接
    。然后
    调用并返回它们的结果(这也是承诺)
  • 您似乎没有理解传递给
    的回调。那么
    将始终在其余代码运行之后异步运行。这就是对象变为空的原因:在任何请求有时间完成之前调用
    resolve
    函数
现在,循环和承诺不能很好地结合,但是有办法处理它们。你需要理解的是,通过循环,你想要的是连锁承诺。Th