You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Node.js使用pixelmatch报错:TypeError: listener参数必须是函数

解决Node.js中使用pixelmatch时的TypeError: "listener" argument must be a function错误

嘿,我一眼就揪出问题所在了——你在给'parsed'事件绑定监听器的时候犯了个新手常踩的小坑:

你写的代码里是这样的:

img1 = fs.createReadStream(screenshot_path).pipe(new PNG()).on('parsed', doneReading())

这里的doneReading()是直接执行了函数,而不是把函数本身作为监听器传递给事件。这就导致事件系统收到的是doneReading执行后的返回值(因为它是async函数,返回的是一个Promise对象),而不是一个可执行的函数,所以才会抛出"listener" argument must be a function这个错误。

第一步:修正监听器绑定

把括号去掉,直接传递函数引用就好:

img1 = fs.createReadStream(screenshot_path).pipe(new PNG()).on('parsed', doneReading)
img2 = fs.createReadStream(backup_path).pipe(new PNG()).on('parsed', doneReading)

第二步:处理async函数的潜在问题

你的doneReading是async函数,在事件监听器里用async函数的话,最好加上错误捕获,不然里面的代码抛出错误(比如expect断言失败)时,可能会变成未捕获的Promise拒绝。可以给它包一层try/catch:

let doneReading = async function(){
  try {
    if(++filesRead < 2) return; 
    var diff = new PNG({width: img1.width, height: img2.height}); 
    var numDiffPixels = pixelmatch(img1.data, img2.data, diff.data, img1.width, img1.height, {threshold: 0.1}) 
    diff.pack().pipe(fs.createWriteStream(diff_path)); 
    expect(numDiffPixels).to.equal(0)
  } catch (err) {
    // 手动抛出错误,让Mocha能捕获到断言失败或其他异常
    throw err;
  }
}

第三步:确保Mocha正确处理异步逻辑

因为compareScreenshots是async函数,在Mocha测试里,只要让函数正常返回Promise就行,Mocha会自动等待Promise完成,不需要额外的done回调。

修正后的完整代码

const fs = require('fs');
const PNG = require('pngjs').PNG;
const pixelmatch = require('pixelmatch');
const { expect } = require('chai');

const compareScreenshots = async (path) => { 
  const diff_path = "./build/diff/" + path + ".diff.png" 
  const screenshot_path = "./build/" + path + ".png" 
  const backup_path = "./backup/" + path +".png" 
  // 确保diff目录存在,避免写入失败
  fs.mkdirSync('./build/diff/', { recursive: true });
  
  let img1, img2, filesRead = 0; 
  let doneReading = async function(){ 
    try {
      if(++filesRead < 2) return; 
      // 提前检查图片尺寸,避免pixelmatch报错
      if (img1.width !== img2.width || img1.height !== img2.height) {
        throw new Error(`截图尺寸不匹配:当前截图(${img1.width}x${img1.height}) vs 备份截图(${img2.width}x${img2.height})`);
      }
      var diff = new PNG({width: img1.width, height: img2.height}); 
      var numDiffPixels = pixelmatch(img1.data, img2.data, diff.data, img1.width, img1.height, {threshold: 0.1}) 
      diff.pack().pipe(fs.createWriteStream(diff_path)); 
      expect(numDiffPixels).to.equal(0);
    } catch (err) {
      throw err;
    }
  } 
  // 修正:传递函数引用,而非直接执行函数
  img1 = fs.createReadStream(screenshot_path).pipe(new PNG()).on('parsed', doneReading);
  img2 = fs.createReadStream(backup_path).pipe(new PNG()).on('parsed', doneReading);
}

额外小提示

  • 我在代码里加了目录检查和尺寸校验,能提前避免一些隐性错误;
  • 如果你的测试用例是Mocha的async风格,记得在调用compareScreenshots时加上await,或者让测试函数返回它的Promise。

内容的提问来源于stack exchange,提问作者A McGuinness

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 07:04:10