2 回答
TA贡献1817条经验 获得超14个赞
await 关键字可以大大简化这一点。您不需要使用自递归函数。此演示使用随机大小的数组伪造服务器调用。
https://jsfiddle.net/mvwahq19/1/
// setup: create a list witha random number of options.
var sourceList = [];
var numItems = 10 + Math.floor(Math.random() * 20);
for (var i = 0; i < numItems; i++)
{
sourceList.push(i);
}
sourceList.push(100);
var currentIndex = 0;
// a function which returns a promise. Imagine it is asking a server.
function getNextItem() {
var item = sourceList[currentIndex];
currentIndex++;
return new Promise(function(resolve) {
setTimeout(function() {
resolve(item);
}, 100);
});
}
async function poll() {
var collection = [];
var done = false;
while(!done) {
var item = await getNextItem();
collection.push(item);
console.log("Got another item", item);
if (item >= 100) {
done = true;
}
}
console.log("Got all items", collection);
}
poll();
你可以写一个普通的 for 循环,除了内容使用 await。
TA贡献1804条经验 获得超8个赞
当我直接问他这个问题时,他用时间和知识支持我并给出了这个很好的答案。
代码:
//// files LINKING (those files do exist on live server - just for testing purposes):
// file1->file4(AND file101)->file7->file10 /-/ file1 content: file4 /-/ file4 content: file7 /-/ file7 content: file10 /-/ file10 content: EMPTY /-/ file101 content: EMPTY
// file2->file5(AND file102)->file8->file11 /-/ file2 content: file5 /-/ file5 content: file8 /-/ file8 content: file11 /-/ file11 content: EMPTY /-/ file102 content: EMPTY
// file3->file6(AND file103)->file9->file12 /-/ file3 content: file6 /-/ file6 content: file9 /-/ file9 content: file12 /-/ file12 content: EMPTY /-/ file103 content: EMPTY
var urls = ['http://czyprzy.vdl.pl/file1.txt', 'http://czyprzy.vdl.pl/file2.txt', 'http://czyprzy.vdl.pl/file3.txt'];
var urlsPromise = [];
function requestPromise(url) {
return new Promise(function(resolve, reject) {
request(url, function (err, resp, content) {
if (err || resp.statusCode != 200) reject(err || resp.statusCode);
else resolve(content);
});
});
}
async function urlContent(url, number) {
var arr = [];
let content = await requestPromise(url);
while (content.indexOf(';') !== -1)
{
var semiColon = content.indexOf(';');
var fileLink = content.slice(content.indexOf('file'), semiColon + 1);
content = content.replace(fileLink, ''); // we need to remove the file link so we won't iterate over it again, we will add to the array only new links
var fileLinkNumber = fileLink.replace('file', '');
fileLinkNumber = fileLinkNumber.replace(';', '');
fileLinkNumber =+ fileLinkNumber;
url = 'http://czyprzy.vdl.pl/file' + fileLinkNumber + '.txt'; // we build new address
arr.push({url, fileLinkNumber});
}
if (content.indexOf('file') !== -1)
{
var fileLinkNumber = content.slice(content.indexOf('file') + 4);
fileLinkNumber =+ fileLinkNumber;
url = 'http://czyprzy.vdl.pl/file' + fileLinkNumber + '.txt';
arr.push({url, fileLinkNumber});
}
var newArr = arr.map(function(item)
{
return urlContent(item.url, item.fileLinkNumber); // return IS important here
});
return [].concat(arr, ...await Promise.all(newArr));
}
async function doing() {
let urlsPromise = [];
for (let i = 0; i < urls.length; i++) {
urlsPromise.push(urlContent(urls[i], i + 1));
}
let results = [].concat(...await Promise.all(urlsPromise)); // flatten the array of arrays
console.log(results);
}
//// this is only to show Promise.all chaining - so you can do async loop, and then wait for some another async data - in proper chain.
var test_a = ['http://czyprzy.vdl.pl/css/1.css', 'http://czyprzy.vdl.pl/css/2.css', 'http://czyprzy.vdl.pl/css/cssa/1a.css', 'http://czyprzy.vdl.pl/css/cssa/2a.css'];
var promisesTest_a = [];
function requestStyle(url)
{
return new Promise(function(resolve, reject)
{
request(url, function(error, response, content)
{
if (response.statusCode === 200 && !error)
{resolve(content);}
else
{reject(error);}
});
});
}
for (var i = 0; i < test_a.length; i++)
{promisesTest_a.push(requestStyle(test_a[i]));}
Promise.all(promisesTest_a).then(function(promisesTest_a)
{
console.log(promisesTest_a);
}).then(function()
{
console.log('\nNow we start with @imports...\n');
}).then(function()
{
return doing();
}).then(function()
{
console.log('ALL DONE!');
});
评论:
首先解释什么是 [...] - 解构的休息参数(以防万一,如果你不知道的话)。
var arr = [];
var array1 = ['one', 'two', 'three']
var array2 = [['four', 'five', ['six', 'seven']], 'eight', 'nine', 'ten'];
arr = array1.concat(array2);
console.log(arr); // it does not flattern the array - it just concatenate them (join them together)
console.log('---');
// however
arr = array1.concat(...array2);
console.log(arr); // notice the [...] - as you can see it flatern the array - 'four' and 'five' are pull out of an array - think of it as level up :) remember that it pull up WHOLE array that is deeper - so 'six' and 'seven' are now 1 level deep (up from 2 levels deep, but still in another array).
console.log('---');
// so
arr = [].concat(...arr);
console.log(arr); // hurrrray our array is flat (single array without nested elements)
console.log();
所有准备下载的文件(链接)(urls数组中的 3 个起始文件)几乎立即下载(同步循环包含它们的数组 - 一个接一个,但很快,因为我们只是迭代它们以同步方式)。
然后,当我们获得它们的内容时(因为我们等待内容下载 - 所以我们在这里得到了一个已解析的承诺数据)我们开始寻找与我们已经获得的相关的其他可能的 url(文件)的信息,以下载它们(通过异步递归)。
当我们找到有关可能的附加 url/文件的所有信息(以正则表达式数组表示 -匹配)时,我们将其推送到数据数组(在我们的代码中命名为arr)并下载它们(感谢 url 的变化)。
我们下载它们通过回归异步 urlContent功能需要等待的requestPromise承诺(所以我们有决心/拒绝的数据urlContent所以如果需要,我们可以变异它-构建正确的URL,以获得下一个文件/内容)。
依此类推,直到我们“迭代”(下载)所有文件。每次调用 urlContent 时,它都会返回一个最初未决的承诺数组(promises 变量)。当我们等待 Promise.all(promises) 时,只有当所有这些承诺都已解决时,才会在那个位置恢复执行。所以,在那一刻,我们拥有每一个承诺的价值。每一个都是一个数组。我们使用一个 big concat 将所有这些数组组合成一个大数组,还包括arr的元素(我们需要记住从我们已经下载的文件中下载的文件可能超过 1 个——这就是我们存储值的原因在数据数组中 -在代码中命名为arr - 存储promiseReques函数解析/拒绝值)。这个“大”数组是用于解决承诺的值。回想一下,这个 promise 是当前函数上下文已经返回的那个,在第一个 await 被执行时。
这是很重要的部分 - 所以它 (urlContent) 返回 (await) 一个单一的承诺,并且这个(返回的)承诺被解析为一个数组作为值。请注意,当遇到第一个 await 时,异步函数会立即将承诺返回给调用者。async 函数中的 return 语句决定了返回的 promise 的解析值是什么。在我们的例子中,这是一个数组。
因此,每次调用时 urlContent 都会返回一个承诺 - 数组中已解析的值 - [...](解构的其余参数 - 返回一个最终解析为数组的承诺),由我们的异步执行函数收集(导致 3 个 url 被触发在开始时 - 每个人都有自己的 urlContent 函数...路径),从 Promise.all(urlsPromise) 收集(等待!)所有这些数组,以及当它们被解析时(我们等待它们被解析并由 Promise 传递) .all)它“返回”您的数据(结果变量)。准确地说,做返回一个承诺(因为它是异步的)。但是我们称之为做的方式,我们表明我们对这个承诺解决的问题不感兴趣,事实上,因为做没有 return 语句,该承诺解析为 UNDEFINED (!)。无论如何,我们不使用它 - 我们只是将结果输出到控制台。
与异步函数混淆的一件事是,当函数返回时不执行return 语句(名称中有什么,对吧!?;)。该函数在执行第一个 await 时已经返回。当最终它执行 return 语句时,它并没有真正返回一个值,而是解析“自己的”承诺;它之前返回的那个。如果我们真的想将输出与逻辑分开,我们不应该在那里做 console.log(results) ,而是要返回结果,然后,在我们称之为做的地方,我们可以做 dodo.then(console.log); 现在我们确实使用了 do 返回的 promise!
我会保留动词“返回”来表示函数的调用者同步返回的内容。我将使用“解决”作为将承诺设置为具有值的已解决状态的操作,该值可以通过await或.then()访问。
添加回答
举报