簡體   English   中英

有沒有辦法在解決諾言之前填充JSON對象

[英]Is there a way to fill the JSON object before resolving the 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++) {
       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
})

所需的輸出應類似於{1234:{text:some parsed text}},但我一直得到的只是一個空對象

您的代碼中有很多事情要解決,所以讓我們一步一步來:

  • 根據您對dbresult[i]使用來看, dbresult似乎是一個數組,但是您還具有i < dbresult條件,這意味着它是整數。 我假設你的意思是i < dbresult.length
  • 您已經在處理諾言的情況下使用new Promise(...) 除非沒有其他選擇,否則永遠不要使用此模式,並且始終嘗試鏈接.then調用並返回其結果(也總是保證)。
  • 您似乎似乎無法理解傳遞給.then的回調在其余代碼運行之后將始終異步運行。 這就是您的對象變空的原因:在任何請求都沒有時間完成之前調用resolve函數。

現在,循環和承諾並不能很好地融合在一起,但是有一些處理方式。 您需要理解的是,通過循環,您想要的是鏈接promises 以這種方式鏈接諾言的方式主要有兩種:命令式方式和功能式方式。

我將專注於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);
}

希望這些評論對您有所幫助。 同樣,與承諾一起循環工作也很糟糕。

不過,有兩種方法可以更輕松地完成此操作! 兩者都使用數組的功能方法。 第一個是最簡單的,它是我推薦的一個,除非數組很大。 它利用了.mapPromise.all兩個強大的盟友:

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並傳遞concurrency值使此操作變得更好。 我認為這實際上是最好的解決方案,但是我想在這里堅持使用香草。

但是,此解決方案的主要問題是所有請求都將立即啟動 這可能意味着,對於非常大的陣列,某些請求僅在隊列中等待,否則您將耗盡進程的套接字限制,具體取決於實現方式。 無論如何,這都不是理想的,但是對於大多數情況而言,它都是可行的。

另一種功能性解決方案包括使用.reduce而不是for ... of循環來復制命令式命令,並且它在答案的末尾實現,更多地是出於好奇,我認為這有點“聰明”碼”。

在我看來, 解決此問題的最佳方法是僅使用async/await並完全忘記promise 在這種情況下,您可以正常編寫循環,並在適當的地方簡單地等待:

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;
}

就這樣,就這么簡單。


現在,對於我認為是“聰明”的解決方案,僅使用.reduce

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);
}

如果還不能立即知道它在做什么,那是正常的。 這與原始命令式then鏈接的功能完全相同,只是使用.reduce而不是手動的for循環。

請注意,我個人不一定會這樣做,因為我認為這有點“聰明”,並且需要一些時間來進行腦力分析。 如果實施這樣的事情( then使用-chaining .reduce非常有用 ,即使有點混亂),請添加評論,解釋為什么你這樣做,它是什么,或者說,可以幫助其他開發人員了解IR乍一看。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM