问一个 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 模块有什么关系吗?
当然可以!你提到的问题是因为 gm.size()
方法是一个异步操作,它需要读取图像文件以获取其尺寸。如果在这个异步回调中调用其他异步操作(如 gm.drawText().write()
),可能会导致一些意外的行为。
让我们逐步分析一下代码,并提供一个更清晰的版本来解决这个问题。
分析问题
-
异步回调嵌套:
- 在第一个代码片段中,
gm.size()
是一个异步方法,它会调用传入的回调函数来返回图像的尺寸。 - 然后,在
size
回调内部,你又调用了另一个异步方法gm.drawText().write()
。 - 这种嵌套的异步操作可能会导致程序挂起或执行顺序混乱。
- 在第一个代码片段中,
-
流程控制:
- 当你在
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();
解释
-
引入
optimist
并设置参数:- 使用
optimist
库来解析命令行参数。
- 使用
-
创建读取流:
- 使用
fs.createReadStream()
创建一个读取流。
- 使用
-
定义异步函数
processImage
:- 使用
async/await
来处理异步操作,使其代码更易读。
- 使用
-
获取图像尺寸:
- 使用
Promise
包装gm.size()
方法,使其支持await
。
- 使用
-
绘制文字并保存图像:
- 使用
Promise
包装gm.drawText().write()
方法,使其支持await
。
- 使用
-
错误处理:
- 使用
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);
}
});
});
解释
- 异步回调:
gm.size()
是一个异步方法,需要在回调函数中处理结果。 - 错误处理:在每个异步操作的回调函数中添加错误处理,确保异常能够被捕获和处理。
- 流处理:由于
gm.size()
可能会消耗流中的数据,因此在调用gm.size()
后,需要重新创建流或使用缓存的数据。
通过这种方式,你可以确保所有操作都在正确的顺序下执行,并且不会导致程序挂起。