链接承诺不使用多个“那么”
我正在学习如何使用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))