链接承诺不使用多个“那么”

我正在学习如何使用Promise。 我有以下函数返回“i”xkcd漫画标题作为承诺:

var xkcd = function(i) { return new Promise( function(resolve, reject) { var tempurl = 'https://www.xkcd.com/' + i; request(tempurl, function(error, response, body) { if (error) reject(error); var $ = cheerio.load(body); resolve($('title').text() + '\n'); }); }); }; 

如果我想要得到前4个冠军,我就把我的.then()这样链接起来:

 var result = ''; xkcd(1) .then(fullfilled => { result += fullfilled; }) .then(() => xkcd(2)) .then(fullfilled => { result += fullfilled; }) .then(() => xkcd(3)) .then(fullfilled => { result += fullfilled; }) .then(() => xkcd(4)) .then(fullfilled => { result += fullfilled; console.log(result); }); 

有没有更好的方法来做到这一点,而不是链接这么多“那么”? 说如果我想得到前50个漫画标题,我将不得不链接很多“那么”。

我可以做到这一点,而不使用使用recursioncallback的Promises:

 function getXKCD(n) { var i = 1; (function getURL(i){ var tempurl = 'https://www.xkcd.com/' + i; request(tempurl, function(error, response, body) { if (error) console.log('error: ' + error); var $ = cheerio.load(body); //prints the title of the xkcd comic console.log($('title').text() + '\n'); i++; if (i <= n) getURL(i); }); })(i); } getXKCD(4); 

但是我很想知道是否可以和Promises做同样的事情。 谢谢。

如果你想获得文章顺序:

 function getSequentially(currentArticle, doUntil) { if (currentArticle !== doUntil) { xkcd(currentArticle) .then(article => getSequentially(currentArtile + 1, doUntil)) } } 

如果你想一次获得所有的文章:

 Promise .all(new Array(AMOUNT_OF_ARTICLES).fill(null).map((nll, i) => xkcd(i + 1))) .then(allArticles => ...); 

我不假装上面的所有内容都可以在复制/粘贴之后正常工作,但这只是您如何执行此操作的一个想法。

你可以把promise传给一个数组,然后返回Promise.all ,当所有的promise都解决了,

 function getXKCD(_start, _end) { if (_end >= _start) return Promise.reject('Not valid!'); var promises = []; (function rec(i) { var p = new Promise(function(resolve, reject) { request('https://www.xkcd.com/' + i, function(error, response, body) { if (error !== null) return reject(error); if (i <= _end) rec(++i); let $ = cheerio.load(body); resolve($('title').text()); }); }); promises.push(p); })(_start); return Promise.all(promises); } getXKCD(1, 50).then(res => { /* All done ! */ }).catch( err => { /* fail */ }) 

有几种方法。 你可以用你需要的所有值填充一个数组,然后用Promise.all()Promise.all()使用.map()

 function getXkcd(count) { // Make an array of the comic numbers using ES6 Array.fill()... var ids = new Array(count).fill(1).map((val, index)=>index+1) // Now you can .map() or .reduce() over this list. } 

还有其他有趣的方法可以解决这个问题。 你可以使用recursion包装来完成这个工作。 保留原来的xkcd函数不变,你可以build立一个recursion调用自己的简单函数…

 function getXkcd(max, last) { var current = last ? last + 1 : 1; xkcd(current) .then(function(title) { // Process the data. result += title; // We don't really care about a return value, here. }) .then(getXkcd.bind(null, max, current)) .catch(function(error) { // We should do something to let you know if stopped. }); } 

这是非常接近你的callback版本。 唯一真正的区别是我们使用bind来传递当前值和最大值而不是闭包。 这有一个好处,它会自动处理从中间开始: getXkcd(50, 15); 这也可以添加到您的callback示例中。

使用闭包让我们保持状态并创build一个recursion调用,这可能是一个更清洁的:

 function getXKCD(max, start) { var result = ""; var getNext = function(id){ // If we are done, return the result if (id > n) { return result; } // Otherwise, keep going. return xkcd(id) .then(function(title){ // Accumulate the title in our closure result result += title; // Send next value return id + 1; }) .then(getNext); } // Kick off the loop return getNext(start || 1); } getXKCD(50).then(function(results){ // Do something with the results }, function(error){ // Tell us what went wrong }); 

getXKCD内部,我们创build了一个函数getNext ,它在Promise链的末尾调用自己。 它像减速器一样工作,序列化请求,并最终返回收集的结果。 这个不使用绑定,而是接受链中前一步的“下一个值”。

使用async-await是最好的方式,国际海事组织:

  (async () => { const result = ''; for(let i = 1; i < 50; i++) { result += await xkcd(i); } return result })().then(result => console.log(result))