【问题标题】:NodeJS Async ProgrammingNodeJS 异步编程
【发布时间】:2022-01-29 07:05:07
【问题描述】:

对使用异步函数进行编程完全陌生。 node.js 也是新的,这可能会增加我的问题。我读了很多书并不断遇到类似的问题,似乎我一直在随机让异步代码的某些部分工作,而其他部分则没有。这是我所拥有的简化版本:

基本上我是在搜索音乐网站,抓取所有结果 (scraper_start.js),然后将其发送到 scrape_individual.js 以收集数据。它目前能够获取所有数据,但是当它下载专辑封面时“为时已晚”。

图像确实会记录到控制台,但只有在返回信息之后。另外,如果您有任何好的资源来学习异步编程,请分享它们——我一直无法找到一个既漂亮又干净的网站,并且提供了足够大的示例以使它们变得现实(例如同时工作的多个异步函数和有时相互依赖)。请也批评我的代码 - 我正在努力学习!

文件 scraper_start.js:

const rp = require('request-promise');
const cheerio = require('cheerio');
const scrape = require('./scrape_individual.js');
const base_url = 'https://www.test.ca';
const url = 'https://www.test.ca/search?mysearchstring';

rp(url)
    .then(function(html)
    {
        const $ = cheerio.load(html);
        var results = []
        
        var hits = $('h3 > a').length;
        console.log("TOTAL HITS: " + hits);

        results = $('h3 > a').map(function(i,v){ return $(v).attr('href'); }).get()
        
        return Promise.all(
            results.map(function(url) 
            {
                return scrape(base_url + url);
            })
        );
    })
    .then(function(my_data) 
    {
        console.log(my_data);

    });

文件 scrape_individual.js:

const rp = require('request-promise');
const cheerio = require('cheerio');
var info = {}


const scrape = function(url)
    {
        return rp(url)
        .then(function(html)
        {
            const $ = cheerio.load(html);
            if (!html.includes('contentType = "Podcast"'))
            {
                info = {
                    title: $('h2.bc-heading:first').text(),
                    img3: null};
                
                img_data($('.bc-image-inset-border').attr('src'))
                    .then(function(v) 
                    { 
                        console.log(v);
                        info.img3 = v; // Log the value once it is resolved
                    })
                    .catch(function(v) {
                    
                    });
                
                return info;
            }
        })
    };
    
function img_data(src) 
{
    return new Promise(function(resolve, reject)
    {
        const { createCanvas, loadImage } = require('canvas');
        
        loadImage(src).then((image) => 
        {
            const canvas = createCanvas(image.width, image.height);
            const ctx = canvas.getContext('2d');
            ctx.drawImage(image, 0, 0);

            resolve(canvas.toDataURL());
        });
    });
}

module.exports = scrape;

更新:带有 ASYNC / AWAIT 的新代码

scraper_start.js:

const rp = require('request-promise');
const cheerio = require('cheerio');
const scrape = require('./scrape_individual.js');
const base_url = 'https://www.test.ca';
const url = 'https://www.test.ca/search?mysearchstring';
var data = [];

async function get_links(url)
{
    let html = await rp(url);
    const $ = cheerio.load(html);
    var results = [];
    
    var hits = $('h3 > a').length;
    console.log("TOTAL HITS: " + hits);
    
    hrefs = $('h3 > a').map(function(i,v){ return $(v).attr('href'); }).get()
    
    await Promise.all(hrefs.map(async (href) =>
        {
            let data_single = await scrape.scrape_book3(base_url + href);
            data.push(data_single);
        }));
    
    //QUESTION AREA 1: This data works great with all info.
    console.log(data);
    return data
}

get_links(url);
//QUESTION AREA 2: This data gets printed before getting the actual data returned.
console.log(data);

scrape_individual.js:

const rp = require('request-promise');
const cheerio = require('cheerio');
var info = {}

//scrape2(url)
module.exports.scrape_individual = scrape2;

async function scrape2(url)
{
    let html = await rp(url);
    const $ = cheerio.load(html);
    
    if (!html.includes('contentType = "Podcast"'))
    {
        let my_image = await img_data($('.bc-image-inset-border').attr('src'));
        
        info = {title: $('h2.bc-heading:first').text(),
                img3: my_image};
                
        //console.log(info);
        return info;
    }
}

async function img_data(src) 
{
    const { createCanvas, loadImage } = require('canvas');
    let image = await loadImage(src);
    const canvas = createCanvas(image.width, image.height);
    const ctx = canvas.getContext('2d');
    ctx.drawImage(image, 0, 0);
    //console.log(canvas.toDataURL());
    return canvas.toDataURL();

}

这段代码现在很好用。也更容易理解。当我试图掌握这一点时,请随时批评。我现在的问题更像是一个一般性的编码问题。

在最终结果(数据)结束的 scraper_start.js 中,我用“QUESTION AREA 1”和“QUESTION AREA 2”标记了两个 cmets

问题区域 1:工作得很好,我认为这是因为它在 async 函数中 问题区域 2:在 async 函数之外,还没有返回对象,因为没有什么可说的 await。有没有办法让它等待?

我的问题很重要。我不能使用等待,因为据我了解它不在异步函数中。这是否意味着如果我想维护重要的订单,我的所有代码都需要在函数中?什么是最佳实践?为什么不将每个函数都称为异步函数?

编辑:修正错别字 edit2:添加了 ASYNC / AWAIT 修改

【问题讨论】:

  • img_data(...).then -> return img_data(...).then(() => {...., return info} 如果你不返回一个 Promise,它就会从你的 Promise 链中分离出来。 IOW:你不能这样做 -> somePromise(); return somePromiseResult,,应该是 return somePromise().then(() => { return somPromiseResult } 公平地说,如果你使用 async / await,promise 更容易推理
  • 谢谢基思。老实说,目前这有点超出我的想象。我确实读过 async / await 但所有这些不同的方式都让我头疼。你认为我应该那样学习并改用那样吗?
  • 使用async / await,对 Promises 有很好的理解当然是个好主意,因为它们之间不是相互独立的。但是,如果您开始大量使用async 编程,我当然会建议您研究一下。还要尽量避免使用catch,就像大多数语言的异常大部分应该在顶层处理一样。此外,不是顶级的 catch 应该很少不重新加注,否则程序流程可能会不同步。
  • 谢谢基思!我花了一些时间完全专注于异步/等待。清理了我的代码。在以另一种方式挣扎之后更容易理解!如果你有时间帮忙,我添加了一些问题!再次感谢您引导我朝这个方向前进!

标签: javascript node.js asynchronous async-await


【解决方案1】:

问题区 2 在数据被捕获之前返回,因为你告诉 JS 最后一个函数没有等待任何人,所以它是同步运行的。 但事实是,它实际上是在等待某人,它在等待 get_links()

所以打印数据的一种方法是:

    async printer(){
    const returnedData = await get_links(URL);
    console.log(returnedData)
    }
    printer();

如果你想使用从异步函数返回的数据,你需要用 await 调用它,所以 JS 知道在继续之前它需要等待一个已解决或被拒绝的承诺,否则它将返回一个 Promise<pending> .所有等待都需要在异步中。 一开始,听起来像是一个无限循环,但实际上并非如此。

例如,在我们的示例中,您不需要任何其他异步来调用打印机()(因为没有其他函数依赖于该函数) 我希望这是有道理的,但是一开始的承诺需要一些时间来消化才能理解它们。 在我看来,异步/等待是理解 Promise 的福气,一旦你了解了它以及它们是如何运作的,你就可以更好地理解解决/拒绝方式

【讨论】:

  • 感谢您抽出宝贵时间提供该代码示例。我肯定是过度思考获得另一个 await 的复杂性。我已经用各种语言编程了很长时间,但从未捕捉到异步方式(几年前我试图通过使用 Ajax 来理解它,但无处可去) .我同意 await 方法让事情变得更容易,我无法相信重写上面的代码是多么容易!
猜你喜欢
  • 2023-04-10
  • 2017-01-19
  • 2016-05-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-28
  • 1970-01-01
  • 2019-06-14
相关资源
最近更新 更多