简体   繁体   English

Javascript-链接2个(或更多)promise数组

[英]Javascript - Chaining 2 (or more) arrays of promises

I have some code that does this: First scrape this array of webpages. 我有一些执行此操作的代码:首先抓取此网页数组。 After that, scrape another array of webpages. 之后,再抓取另一组网页。

The following code does what I expect: 以下代码符合我的期望:

let bays=[];
let promises=promisesN=[];

for (let y=2019;y>=2015;y--) 
    promises.push(new Promise(resolve=>
        curl.get(`/*url*/${y}.html`,null, (error,resp,body)=> 
            resp.statusCode==200? resolve(parse(body)):reject(error)
    )));
Promise.all(promises).then(()=>{
    bays.forEach(bay=>{
        if (bay.no.match(/\d+/)<=103) return;
        promisesN.push(new Promise(resolve=>
            curl.get(`/*url*/${bay.code}/`,null, (error,resp,body)=> 
                resp.statusCode==200? resolve(image(bey,body)):reject(error)
    )))});
    Promise.all(promisesN).then(()=>{
        bays.sort((a,b)=>{return parseInt(a.no.match(/\d+/))<parseInt(b.no.match(/\d+/))? -1:1});
        console.log(bays);
    });
}).catch(error=>console.log(error));`

So I've read you can write a simplier nesting-free syntax: 因此,我读过您可以编写一个更简单的无嵌套语法:

doSomething()
.then(function(result) {
  return doSomethingElse(result);
})
.then(function(newResult) {
  return doThirdThing(newResult);
})
.then(function(finalResult) {
  console.log('Got the final result: ' + finalResult);
})
.catch(failureCallback);

How to apply this to the code above? 如何将此应用到上面的代码?

correctness 正确性

let promises=promisesN=[];

This is really incorrect. 这确实是不正确的。 It makes both variables reference the same array, and makes promisesN an implicit global. 它使两个变量都引用同一数组,并使promisesN成为隐式全局变量。 The fact that it appears to work means you aren't in strict mode. 它似乎有效的事实意味着您不处于严格模式。 Always use strict mode . 始终使用严格模式 The correct version of what you intended is: 您想要的正确版本是:

let promises = [];
let promisesN = [];

cleanliness 清洁度

new Promise(resolve=>
    curl.get(`/*url*/${y}.html`,null, (error,resp,body)=> 
        resp.statusCode==200? resolve(parse(body)):reject(error)
))

You're repeating this pattern, so make it into a function, or use a package that does the job for you, like request-promise[-native] or axios . 您要重复这种模式,因此将其变成一个函数,或者使用一个可以为您完成工作的软件包,例如request-promise [ -native ]axios (Also, please show your real code. reject isn't defined here.) (此外,请显示您的真实代码。此处未定义reject 。)

const getAsync = url => new Promise((resolve, reject) => {
    curl.get(url, null, (error, resp, body) => {
        if (resp.statusCode === 200) {
            resolve(body);
        } else {
            reject(error);
        }
    });
});

Notice how you're free to make the function more readable when it isn't repeated, and to extend it later. 请注意,如何在不重复使用该功能时使它更具可读性,并在以后进行扩展。

let promises = [];
let promisesN = [];

for (let y = 2019; y >= 2015; y--) {
    promises.push(getAsync(`/*url*/${y}.html`).then(parse));
}

Promise.all(promises).then(bays => {
    bays.forEach(bay => {
        if (bay.no.match(/\d+/) <= 103) return;
        promisesN.push(getAsync(`/*url*/${bay.code}/`).then(body => image(bay, body)));
    });

    Promise.all(promisesN).then(() => {
        bays.sort((a, b) => {return parseInt(a.no.match(/\d+/)) < parseInt(b.no.match(/\d+/)) ? -1 : 1;});
        console.log(bays);
    });
}).catch(error => console.log(error));

I had to take a few guesses at what your real code looks like again, because you're surely doing something with the resolved value of Promise.all(promises) . 我不得不对您的真实代码再次看起来有些猜测,因为您肯定会使用Promise.all(promises)的解析值来Promise.all(promises) It doesn't have any easily-accessible side-effects. 它没有任何易于获得的副作用。 bey also seemed likely enough to be bay . bey似乎也很有可能成为bay

Now you can give promisesN a more appropriate scope: 现在您可以给promisesN一个更合适的范围:

let promises = [];

for (let y = 2019; y >= 2015; y--) {
    promises.push(getAsync(`/*url*/${y}.html`).then(parse));
}

Promise.all(promises).then(bays => {
    let promisesN = bays
        .filter(bay => bay.no.match(/\d+/) > 103)
        .map(bay => getAsync(`/*url*/${bay.code}/`).then(body => image(bay, body)));

    Promise.all(promisesN).then(() => {
        bays.sort((a, b) => {return parseInt(a.no.match(/\d+/)) < parseInt(b.no.match(/\d+/)) ? -1 : 1;});
        console.log(bays);
    });
}).catch(error => console.log(error));

and use an expression-bodied arrow function where appropriate, since you're already using them whenever they aren't appropriate: 只要他们适合在适当情况下使用表达式健全箭头功能,因为你已经使用它们:

bays.sort((a, b) => parseInt(a.no.match(/\d+/)) < parseInt(b.no.match(/\d+/)) ? -1 : 1);

Now, if my guess about bays is right, then you can't unnest. 现在,如果我对bays猜想是正确的,那么您就可以放心。 If it comes from somewhere else then you can. 如果它来自其他地方,那么您可以。 Normally I would leave a comment about that but I already wrote all this, so… please clarify that for further cleanup. 通常,我会对此发表评论,但是我已经写了所有这些内容,因此……请澄清一下以便进一步清理。

If you're looking to simplify your code, you might consider the use of async/await instead of promises. 如果您想简化代码,则可以考虑使用async / await而不是promise。

The async/await syntax will greatly simplify the presentation and ease comprehension of the code, especially given that your logic relies on asynchronous iteration of arrays. async / await语法将大大简化表示过程并简化代码的理解,特别是考虑到您的逻辑依赖于数组的异步迭代。

Consider the following code revision of your code: 考虑以下代码修订版:

/* Define local helper that wraps curl() in async function declaration */
function async doRequest(url) {

    return (await new Promise(resolve=> curl.get(url, null, (error,resp,body) => 
        resp.statusCode==200 ? resolve(res) : reject(error))))
}

/* Re-define simplified scrape logic using await/async */
function async doScrape() {

    try {

        var bays = []

        /* Iterate date range asynchronously */
        for (let y=2019; y>=2015; y--)  {

            /* Use doRequest helper function to fetch html */
            const response = await doRequest(`/*url*/${y}.html`)

            const bay = parse(response)
            bays.push(bay)
        }

        /* Iterate bays array that was obtained */
        for(const bay of bays) {

            /* Use doRequest helper again to fetch data */
            const response = await doRequest(`/*url*/${bay.code}/`)

            /* Await may not be needed here */
            await image(bay, response) 
        }

        /* Perform your sort (which is non asynchronous) */
        bays.sort((a,b)=> parseInt(a.no.match(/\d+/))<parseInt(b.no.match(/\d+/))? -1:1);

        console.log("Result", bays);
    }
    catch(err) {

        /* If something goes wrong we arrive here - this is 
        essentially equivalent to your catch() block */
        console.error('Scrape failed', err);
    }
}

/* Usage */
doScrape()

Hope that helps! 希望有帮助!

Not entirely sure if this is what you want, but I've separated your code out a bit because I found it easier for me to read. 不完全确定这是否是您想要的,但是我将您的代码分离了一点,因为我发现它更易于阅读。

let bays = [];
let promises = [];
let promisesN = [];

for (let y = 2019; y >= 2015; y--) {
  const promiseOne = new Promise((resolve, reject) => {
    return curl.get(`/*url*/${y}.html`, null, (error, resp, body) => {
      resp.statusCode === 200 ? resolve(parse(body)) : reject(error);
    });
  });
  promises.push(promiseOne);
}

Promise.all(promises)
  .then(() => {
    bays.forEach((bay) => {
      if (bay.no.match(/\d+/) <= 103) {
        return;
      }

      const promiseTwo = new Promise((resolve, reject) => {
        return curl.get(`/*url*/${bay.code}/`, null, (error, resp, body) => {
          resp.statusCode === 200 ? resolve(image(bay, body)) : reject(error);
        });
      });

      promisesN.push(promiseTwo);
    });

    return Promise.all(promisesN);
  })
  .then(() => {
    bays.sort((a, b) => {
      return parseInt(a.no.match(/\d+/), 10) < parseInt(b.no.match(/\d+/), 10) ? -1 : 1;
    });
    console.log(bays);
  })
  .catch((error) => {
    console.log(error);
  });

I am wondering though, you are firing the promises instantly on each iteration of your for loop. 我想知道,您是否在for循环的每次迭代中立即兑现承诺。 This might be intentional, but it means if those promises resolve before the code gets to execute Promise.all you may run into issues. 这可能是有意为之,但这意味着如果这些承诺在代码开始执行Promise之前就解决了,那么您可能会遇到问题。 I personally would do something like, eg const promiseOne = () => somePromise, that way you can create a bunch of promises, and then once they're all created, map over that array and fire them at once. 我个人会做类似const promiseOne =()=> somePromise的操作,这样,您可以创建一堆promise,然后在它们全部创建后,映射到该数组并立即将其触发。 Same thing goes for the second promises. 第二个承诺也是如此。

Not sure if this is helpful, let me know if it is. 不确定是否有帮助,请告诉我是否有帮助。 Feel free to ask more questions too. 也可以随意提出更多问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM