在不使用多个 "then's" 的情况下链接 Promise
Chaining Promises without using multiple "then's"
我正在学习如何使用 Promises。我有以下功能,returns 第 "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);
});
有没有更优雅的方法来做到这一点而无需链接那么多 "then"?假设我想获得前 50 个漫画标题,我将不得不链接很多 "then"s。
我可以在不使用 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 做同样的事情。谢谢。
您可以将 promise 推送到一个数组,然后 return 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 */ })
如果要按顺序获取文章:
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 => ...);
我不假装上面的所有内容在复制/粘贴后都能正常工作,这只是您如何执行此操作的一个想法。
有几种方法。您可以用您需要的所有值填充一个数组,然后使用 .map()
和 Promise.all()
或 .reduce()
以便它们按顺序发生:
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.
}
您也可以通过其他有趣的方式来解决这个问题。您可以使用递归包装器来完成这项工作。保留原始 xkcd
函数不变,您可以构建一个递归调用自身的简单函数...
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.
});
}
这与您的回调版本非常接近。唯一真正的区别是我们使用 bind
来传递当前值和最大值而不是闭包。这样做的好处是它会自动从中间开始处理:getXkcd(50, 15);
这也可以添加到您的回调示例中。
使用闭包让我们保持状态并创建递归调用,这可能更简洁:
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
中,我们创建了一个函数 getNext
,它在 Promise 链的末尾调用自身。它像 reducer 一样工作,序列化请求,并最终 returns 收集结果。这个不使用绑定,但接受链中上一步的 "next value"。
使用 async-await
是最好的方法,IMO:
(async () => {
const result = '';
for(let i = 1; i < 50; i++) {
result += await xkcd(i);
}
return result
})().then(result => console.log(result))
我正在学习如何使用 Promises。我有以下功能,returns 第 "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);
});
有没有更优雅的方法来做到这一点而无需链接那么多 "then"?假设我想获得前 50 个漫画标题,我将不得不链接很多 "then"s。
我可以在不使用 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 做同样的事情。谢谢。
您可以将 promise 推送到一个数组,然后 return 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 */ })
如果要按顺序获取文章:
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 => ...);
我不假装上面的所有内容在复制/粘贴后都能正常工作,这只是您如何执行此操作的一个想法。
有几种方法。您可以用您需要的所有值填充一个数组,然后使用 .map()
和 Promise.all()
或 .reduce()
以便它们按顺序发生:
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.
}
您也可以通过其他有趣的方式来解决这个问题。您可以使用递归包装器来完成这项工作。保留原始 xkcd
函数不变,您可以构建一个递归调用自身的简单函数...
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.
});
}
这与您的回调版本非常接近。唯一真正的区别是我们使用 bind
来传递当前值和最大值而不是闭包。这样做的好处是它会自动从中间开始处理:getXkcd(50, 15);
这也可以添加到您的回调示例中。
使用闭包让我们保持状态并创建递归调用,这可能更简洁:
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
中,我们创建了一个函数 getNext
,它在 Promise 链的末尾调用自身。它像 reducer 一样工作,序列化请求,并最终 returns 收集结果。这个不使用绑定,但接受链中上一步的 "next value"。
使用 async-await
是最好的方法,IMO:
(async () => {
const result = '';
for(let i = 1; i < 50; i++) {
result += await xkcd(i);
}
return result
})().then(result => console.log(result))