2017-01-21 123 views
0

請原諒這個相當具體的問題,但我認爲一般的最終目標可能對其他人有用。在Node中,如何使用promise從多個URL請求JSON?

目標:使用從多個JSON API URL請求的數據填充MongoDB。

短的問題:到目前爲止,我已經取得了一些成功request-promise,它採用藍鳥:

var rp = require('request-promise'); 
var options = { 
    uri: 'http://www.bbc.co.uk/programmes/b006qsq5.json', 
    headers: { 
     'User-Agent': 'Request-Promise' 
    }, 
    json: true 
}; 

rp(options) 
    .then(function (body) { 
     // Mongoose allows us query db for existing PID and upsert 
     var query = {pid: body.programme.pid}, 
      update = { 
       name: body.programme.title, 
       pid: body.programme.pid, 
       desc: body.programme.short_synopsis 
      }, 
      options = { upsert: true, new: true }; 

     // Find the document 
     Programme.findOneAndUpdate(query, update, options, function(err, result) { 
      if (err) return res.send(500, { error: err }); 
      return res.send("succesfully saved"); 
     }); 
    }) 
    .catch(function (err) { 
     return res.send(err); 
    }) 

但我怎麼遍歷的URL的數組,如果沒有程序失敗如果任何承諾被拒絕? 例如,使用Bluebird的情況下,如果任何URL錯誤都會失敗。

const urls = ['http://google.be', 'http://google.uk'] 

Promise.map(urls, rp) 
    .map((htmlOnePage, index) => { 
    return htmlOnePage; 
    }) 
    .then(console.log) 
    .catch((e) => console.log('We encountered an error' + e)); 

,因爲我想寫信給DB與成功的請求,而忽略那些可能不響應正確的話,我需要的東西,跳過拒絕承諾,這.all不做。

長問題: 我一直在閱讀關於承諾的一整天,它讓我頭痛!但是我發現了一些很好的資源,例如https://pouchdb.com/2015/05/18/we-have-a-problem-with-promises.html,其中提到了使用Promise工廠。這是否適合我的情況?我最初認爲我應該提出每個請求,處理結果並將其添加到數據庫,然後轉到下一個請求;但看到.all我以爲我應該做所有的請求,將結果保存在一個數組中,並通過我的數據庫保存功能循環。

我是否應該使用承諾呢?也許我應該只使用async.js之類的東西,然後連續運行我的請求。

非常感謝您的幫助或意見。

+1

如果請求是不依賴於彼此沒有任何理由,你需要的一切()做數據庫更新 – charlietfl

+0

我認爲你可以使用async.eachSeries與承諾。即使出現錯誤 http://caolan.github.io/async/docs.html#eachOfSeries –

+0

@AsifSaeed與async.js混合承諾不建議 –

回答

1

但我怎麼遍歷的URL的數組,如果沒有程序如果有任何承諾都將被拒絕失敗?

如果從.catch比其他被拒絕的承諾返回一個值,你會返回一個解決承諾

所以,你的。然後針對每個單獨的請求會返回一個對象像

{ 
    success: true, 
    result: whateverTheResultIs 
} 

和你釣到的魚返回

{ 
    success: false, 
    error: whateverTheErrorIs 
} 

真的是你不需要的成功特性,它是一種方便,雖然

因此,代碼會假設process(url)返回承諾

Promise.map(urls, url => 
    process(url) 
    .then(result => ({result, success:true})) 
    .catch(error => ({error, success:false})) 
) 
.then(results => { 
    let succeeded = results.filter(result => result.success).map(result => result.result); 
    let failed = results.filter(result => !result.success).map(result => result.error); 
}); 

或者,在ES5

​​
+0

非常感謝這非常全面的解釋!代碼閱讀很好,對我來說很有意義。 –

-1

我只是使用請求,並寫入我自己的諾言與try catch只能解決。下面的僞示例

var request = require('request') 

var urls = ['http://sample1.com/json', 'http://sample2.com/json'] 

var processUrl = (url) => { 
    return new Promise((resolve,reject)=> { 
    var result; 
    try { 
     var myRequest = { 
      uri: url, 
      method: 'GET', 
      header: {...} 
     }; 
     request(option, (res,body,err)=> { 
      if(err) { 
       result = err; 
       return; 
      } 
      result = body; 
     }) 
    } 
    catch(e) { 
     result = e; 
    } 
    finally { 
     resolve(result) 
    } 
    }) 
} 
1

我認爲您的問題不是關於藍鳥api而是構建您的承諾鏈。

const reducePropsToRequests = (props) => Promise.resolve(Object 
    .keys(props) 
    .reduce((acc, key) => { 
    acc[key] = request(sources[key]); 
    return acc; 
    }, {})); 

const hashToCollection = (hash) => Promise.resolve(Object 
    .keys(hash) 
    .reduce((acc, k) => { 
    return [...acc, {source: k, data: hash[k]}]; 
    }, [])); 

const fetchFromSources = (sources) => Promise.props(sources); 

const findSeveralAndUpdate = (results) => Promise 
    .each(results.map(obj => { 
    // you have access to original {a: 'site.com'} 
    // here, so use that 'a' prop to your advantage by abstracting out 
    // your db config somewhere outside your service 
    return Programme.findOneAndUpdate(someConfig[obj.source], obj.data); 
    })) 

const requestFromSeveralAndUpdate = (sources) => reducePropsToRequests(sources) 
    .then(fetchFromSources) 
    .then(hashToCollection) 
    .then(findSeveralAndUpdate) 
    .catch(/* some err handler */); 

requestFromSeveralAndUpdate({ a: 'site.com', b: 'site.net' }); 
+0

此答案嘿感謝環路不會停止,更好的結構正是我在找。我只是有一些麻煩的是它..我得到了'要求(來源[關鍵])',應該說是'道具[關鍵]'錯誤?此外,它似乎並沒有處理錯誤的請求(例如404),我不知道我會在哪裏處理。日Thnx –

1

我不知道是否適合這個你的情況,但我認爲你可以使用一個計數器來檢查時,所有的承諾又回來了,不管事實,即每一個已經解決或拒絕

var heroes = [ 
 
    'Superman', 
 
    'Batman', 
 
    'Spiderman', 
 
    'Capitan America', 
 
    'Ironman', 
 
]; 
 

 
function getHero(hero) { 
 
    return new Promise((resolve, reject) => { 
 
    setTimeout(() => { 
 
     return Math.round(Math.random()) ? resolve(hero + ' lives') : reject(hero + ' dead'); 
 
    }, Math.random() * 3000)  
 
    }) 
 
} 
 

 
function checkHeroes() { 
 
    var checked = heroes.length; 
 
    heroes.forEach((hero) => { 
 
    getHero(hero) 
 
    .then((res) => { 
 
     checked --; 
 
     console.log(res); 
 
     if (!checked) done(); 
 
    }) 
 
    .catch((err) => { 
 
     checked --; 
 
     console.log(err); 
 
     if (!checked) done();  
 
    });   
 
    }) 
 
} 
 

 
function done() { 
 
    console.log('All heroes checked'); 
 
} 
 

 
checkHeroes();

+0

一個使用承諾的一點是,你可以使用像'Promise.all工具()'多的承諾,而不是設計專櫃自己重建已經存在的功能。當你沒有承諾時,可以使用這樣的計數器,但是當你有承諾時真的不應該這樣做。此外,由於這是藍鳥的承諾,有喜歡的工具'Promise.map()'將迭代集合爲您和返回跟蹤所有結果一個承諾。 – jfriend00

+0

我只是堅持標準的Promise實現。 –

+0

'Promise.all()'是標準的承諾的實現。但是,問題是專門標有藍鳥和藍鳥使用這樣它簡化了事情的最好的答案會利用這一點。 – jfriend00