异步承诺使用未知的异步值提供递归

我对异步承诺递归感到头疼。我有一堆承诺在下载异步数据时解决(由 Promise.all 组合)。但有时在我刚刚下载的数据中有指向另一个数据的链接,那必须是下载(递归)。最好的解释是显示我猜的代码。注释在代码中。(我尝试了各种组合都无济于事。)


var urls = ['http://czyprzy.vdl.pl/file1.txt', 'http://czyprzy.vdl.pl/file2.txt', 'http://czyprzy.vdl.pl/file3.txt'];

var urlsPromise = [];

var secondPart = [];

var thirdPart = [];


function urlContent(url, number) {

  return new Promise(function (resolve) {

    var dl = request(url, function (err, resp, content) {

      if (err || resp.statusCode >= 400) {

        return resolve({number : number, url : url, error : 'err'});

      }

      if (!err && resp.statusCode == 200) {

        if (content.indexOf('file') !== -1) // if there is 'file' inside content we need (would like to :) download this new file by recursion

        {

            content = content.slice(content.indexOf('file') + 4);

            content =+ content; // (number to pass later on, so we know what file we are working on)

            url = 'http://czyprzy.vdl.pl/file' + content + '.txt'; // (we build new address)


            //urlsPromise.push(urlContent(url, content)); // this will perform AFTER Promise.all(urlsPromise) so we simply can't do recurention (like that) here

            secondPart.push(urlContent(url, content)); // if we use another promise array that put resolved items to that array everything will work just fine - but only till first time, then we would need to add another (thirdPart) array and use another Promise.all(thirdPart)... and so on and so on... --- the problem is I don't know how many files there will be, so it means I have no idea how many 'parts' for Promise.all I need to create, some kind of asynchronous loop/recursion would save me here, but I don't know how to do that properly so the code can run in proper order

        }

        return resolve({number : number, url : url}); // this goes to 'urlsPromise' array

      }

    });

  });

}


if (urls.length !== 0) {

  for (var i = 0; i < urls.length; i++)

  {urlsPromise.push(urlContent(urls[i], i + 1));}

}


幕布斯7119047
浏览 137回答 2
2回答

大话西游666

await 关键字可以大大简化这一点。您不需要使用自递归函数。此演示使用随机大小的数组伪造服务器调用。https://jsfiddle.net/mvwahq19/1/// setup: create a list witha&nbsp; random number of options.var sourceList = [];var numItems = 10 + Math.floor(Math.random() * 20);for (var i = 0; i < numItems; i++){&nbsp; &nbsp; sourceList.push(i);}sourceList.push(100);var currentIndex = 0;// a function which returns a promise. Imagine it is asking a server.function getNextItem() {&nbsp; &nbsp; var item = sourceList[currentIndex];&nbsp; &nbsp; currentIndex++;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp; return new Promise(function(resolve) {&nbsp; &nbsp; &nbsp; &nbsp; setTimeout(function() {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; resolve(item);&nbsp; &nbsp; &nbsp; &nbsp; }, 100);&nbsp; &nbsp; });}async function poll() {&nbsp; &nbsp; var collection = [];&nbsp; &nbsp; var done = false;&nbsp; &nbsp; while(!done) {&nbsp; &nbsp; &nbsp; &nbsp; var item = await getNextItem();&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; collection.push(item);&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; console.log("Got another item", item);&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; if (item >= 100) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; done = true;&nbsp; &nbsp; &nbsp; &nbsp; }&nbsp; &nbsp; }&nbsp; &nbsp; console.log("Got all items", collection);}poll();你可以写一个普通的 for 循环,除了内容使用 await。

胡说叔叔

当我直接问他这个问题时,他用时间和知识支持我并给出了这个很好的答案。代码://// files LINKING (those files do exist on live server - just for testing purposes):// file1->file4(AND file101)->file7->file10&nbsp; /-/ file1 content: file4 /-/ file4 content: file7 /-/ file7 content: file10 /-/ file10 content: EMPTY /-/ file101 content: EMPTY// file2->file5(AND file102)->file8->file11&nbsp; /-/ file2 content: file5 /-/ file5 content: file8 /-/ file8 content: file11 /-/ file11 content: EMPTY /-/ file102 content: EMPTY// file3->file6(AND file103)->file9->file12&nbsp; /-/ file3 content: file6 /-/ file6 content: file9 /-/ file9 content: file12 /-/ file12 content: EMPTY /-/ file103 content: EMPTYvar urls = ['http://czyprzy.vdl.pl/file1.txt', 'http://czyprzy.vdl.pl/file2.txt', 'http://czyprzy.vdl.pl/file3.txt'];var urlsPromise = [];function requestPromise(url) {&nbsp; &nbsp; return new Promise(function(resolve, reject) {&nbsp; &nbsp; &nbsp; &nbsp; request(url, function (err, resp, content) {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; if (err || resp.statusCode != 200) reject(err || resp.statusCode);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; else resolve(content);&nbsp; &nbsp; &nbsp; &nbsp; });&nbsp; &nbsp; });}async function urlContent(url, number) {&nbsp; var arr = [];&nbsp; let content = await requestPromise(url);&nbsp; while (content.indexOf(';') !== -1)&nbsp; {&nbsp; &nbsp; 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 linksvar fileLinkNumber = fileLink.replace('file', '');fileLinkNumber = fileLinkNumber.replace(';', '');fileLinkNumber =+ fileLinkNumber;url = 'http://czyprzy.vdl.pl/file' + fileLinkNumber + '.txt'; // we build new addressarr.push({url, fileLinkNumber});}if (content.indexOf('file') !== -1){var fileLinkNumber = content.slice(content.indexOf('file') + 4);fileLinkNumber =+ fileLinkNumber;&nbsp; url = 'http://czyprzy.vdl.pl/file' + fileLinkNumber + '.txt';&nbsp; 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() {&nbsp; &nbsp; let urlsPromise = [];&nbsp; &nbsp; for (let i = 0; i < urls.length; i++) {&nbsp; &nbsp; &nbsp; &nbsp; urlsPromise.push(urlContent(urls[i], i + 1));&nbsp; &nbsp; }&nbsp; &nbsp; let results = [].concat(...await Promise.all(urlsPromise)); // flatten the array of arrays&nbsp; &nbsp; 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){&nbsp; &nbsp;console.log(promisesTest_a);}).then(function(){&nbsp; &nbsp;console.log('\nNow we start with @imports...\n');}).then(function(){&nbsp; &nbsp;return doing();}).then(function(){&nbsp; &nbsp;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('---');// howeverarr = 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('---');// soarr = [].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()访问。
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

JavaScript