问一个 Nodejs 的问题

问一个 Nodejs 的问题

node 新手一枚,我使用了 gm 库,但是使用的时候遇到一个问题:

下面这段代码不能退出,会挂起,也没有 console.log 输出:

#!/usr/bin/env node
var fs = require('fs');
var gm = require('gm');
var argv = require('optimist')
	.demand(1)
	.demand('l')
	.alias('l', 'label')
	.describe('l', 'Specify a label')
	.argv;

var file = argv._[0]; var label = argv[‘label’]; var stream = fs.createReadStream(file); var magic = gm(stream);

magic.size(function (err, size) { magic.drawText(40, 30, label).write(’./defaults.png’, function (err) { if (!err) console.log(‘Done!’); }); });

但下面这段代码可以正常工作:

#!/usr/bin/env node
var fs = require('fs');
var gm = require('gm');
var argv = require('optimist')
	.demand(1)
	.demand('l')
	.alias('l', 'label')
	.describe('l', 'Specify a label')
	.argv;
 
var file = argv._[0];
var label = argv['label'];
var stream = fs.createReadStream(file);
var magic = gm(stream);
 
magic.drawText(40, 30, label).write('./defaults.png', function (err) {
	if (!err) console.log('Done!');
});

gm.size() 方法应该是异步的,为什么把逻辑写到异步方法里面会导致挂起呢?和 fs 模块有什么关系吗?


6 回复

当然可以!你提到的问题是因为 gm.size() 方法是一个异步操作,它需要读取图像文件以获取其尺寸。如果在这个异步回调中调用其他异步操作(如 gm.drawText().write()),可能会导致一些意外的行为。

让我们逐步分析一下代码,并提供一个更清晰的版本来解决这个问题。

分析问题

  1. 异步回调嵌套

    • 在第一个代码片段中,gm.size() 是一个异步方法,它会调用传入的回调函数来返回图像的尺寸。
    • 然后,在 size 回调内部,你又调用了另一个异步方法 gm.drawText().write()
    • 这种嵌套的异步操作可能会导致程序挂起或执行顺序混乱。
  2. 流程控制

    • 当你在 gm.size() 回调中进行其他操作时,可能会出现流程控制的问题,导致后续操作没有正确执行。

解决方案

为了更好地管理异步操作,我们可以使用 .then() 方法来处理异步操作,或者使用 async/await 来使代码更加清晰易读。

使用 async/await

#!/usr/bin/env node
const fs = require('fs');
const gm = require('gm');

// 引入 optimist 库并设置参数
const argv = require('optimist')
    .demand(1)
    .demand('l')
    .alias('l', 'label')
    .describe('l', 'Specify a label')
    .argv;

const file = argv._[0];
const label = argv['label'];

const stream = fs.createReadStream(file);

async function processImage() {
    try {
        const magic = gm(stream);
        
        // 获取图像尺寸
        const size = await new Promise((resolve, reject) => {
            magic.size((err, size) => {
                if (err) reject(err);
                else resolve(size);
            });
        });

        // 绘制文字并保存图像
        await new Promise((resolve, reject) => {
            magic.drawText(40, 30, label)
                .write('./defaults.png', (err) => {
                    if (err) reject(err);
                    else resolve();
                });
        });

        console.log('Done!');
    } catch (error) {
        console.error('Error:', error);
    }
}

processImage();

解释

  1. 引入 optimist 并设置参数

    • 使用 optimist 库来解析命令行参数。
  2. 创建读取流

    • 使用 fs.createReadStream() 创建一个读取流。
  3. 定义异步函数 processImage

    • 使用 async/await 来处理异步操作,使其代码更易读。
  4. 获取图像尺寸

    • 使用 Promise 包装 gm.size() 方法,使其支持 await
  5. 绘制文字并保存图像

    • 使用 Promise 包装 gm.drawText().write() 方法,使其支持 await
  6. 错误处理

    • 使用 try/catch 结构来捕获并处理可能发生的错误。

通过这种方式,你可以更好地管理和控制异步操作,避免代码挂起的问题。希望这能帮助你解决问题!


不明白,顶一个

谢谢!解决了,官方文档没太看仔细,应该在 size 方法额外再传入一个参数才行。

Not enough non-option arguments: got 0, need at least 1
什么参数?

哦,这个参数是指一个文件,脚本要处理的文件,谢谢你啦

从你的描述来看,问题出在 gm.size() 方法的使用方式上。gm.size() 是一个异步方法,它用于获取图片的尺寸信息。当你将其他操作(如 gm.drawText().write())写在这个异步回调函数中时,如果第一个异步操作没有完成,程序可能不会按预期执行。

在这段代码中,问题出现在 gm.size() 方法调用后的回调函数中。当 gm.size() 被调用时,它会异步读取图像文件并返回尺寸信息。然而,由于 gm.size() 方法是异步的,你需要确保在此之后的操作在回调函数中执行。

下面是修改后的代码示例:

#!/usr/bin/env node
var fs = require('fs');
var gm = require('gm');
var argv = require('optimist')
    .demand(1)
    .demand('l')
    .alias('l', 'label')
    .describe('l', 'Specify a label')
    .argv;

var file = argv._[0];
var label = argv['label'];
var stream = fs.createReadStream(file);

gm(stream).size(function (err, size) {
    if (err) {
        console.error('Error getting image size:', err);
        return;
    }

    // 在 size 回调中进行后续操作
    gm(stream)
        .drawText(40, 30, label)
        .write('./defaults.png', function (err) {
            if (!err) {
                console.log('Done!');
            } else {
                console.error('Error writing image:', err);
            }
        });
});

解释

  1. 异步回调gm.size() 是一个异步方法,需要在回调函数中处理结果。
  2. 错误处理:在每个异步操作的回调函数中添加错误处理,确保异常能够被捕获和处理。
  3. 流处理:由于 gm.size() 可能会消耗流中的数据,因此在调用 gm.size() 后,需要重新创建流或使用缓存的数据。

通过这种方式,你可以确保所有操作都在正确的顺序下执行,并且不会导致程序挂起。

回到顶部