cli
是libsquoosh的命令行工具,可以直接在命令行中直接运行。
Cli的核心流程分析
cli使用commander
包来实现命令行界面,核心函数是processFiles
,执行的关键流程是
- 读文件
const buffer = await fsp.readFile(file);
- 解码得到image 的原始data,也就是未压缩的数据:
const image = imagePool.ingestImage(buffer);
await image.decoded;
ingestImage
会创建一个新的Image
对象,该对象的构造函数会通过WorkerPool
的dispatchJob
post 一个decode
的message到WorkerPool
的jobQueue
中,WorkerPool
则pick 一个worker
,并用这个worker
来做实际的解码;dispatchJob
是一个异步操作, 内部会创建一个新的Promise
对象,该对象会在图像解码的完成的时候,把解码得到的buffer作为返回值返回,也即该对象的实际结果。
await image.decoded
就是等待解码完成。
- 解析
preprocessors
的选项:
const preprocessOptions = {};
for (const preprocessorName of Object.keys(preprocessors)) {
if (!program.opts()[preprocessorName]) {
continue;
}
preprocessOptions[preprocessorName] = JSON5.parse(
program.opts()[preprocessorName],
);
}
这部分主要是检查是否有preprocessors
的相关参数,有的话就构造一个option.
- 调用
preprocess
for (const image of decodedFiles) {
image.preprocess(preprocessOptions);
}
await Promise.all(decodedFiles.map((image) => image.decoded));
注意image.preprocess
是一个async函数,但他本身的结果或者返回值本身并不重要。它内部调用this.workerPool.dispatchJob
让worker_pool来做实际的preprocess
操作,并把对应的Promise
赋值给image.decoded
。因此在调用preprocess
之后,还需要再调用await Promise.all
来等待所有image.decoded
的完成。
- 调用实际的encoder
const job = image.encode(encodeOptions).then(async () => {
jobsFinished++;
const outputPath = path.join(
program.opts().outputDir,
path.basename(originalFile, path.extname(originalFile)) +
program.opts().suffix,
);
for (const output of Object.values(image.encodedWith)) {
const outputFile = `${outputPath}.${(await output).extension}`;
await fsp.writeFile(outputFile, (await output).binary);
results
.get(image)
.outputs.push(Object.assign(await output, { outputFile }));
}
progress.setProgress(jobsFinished, jobsStarted);
});
jobs.push(job);
注意job
是一个Promise
对象,它是由then()
里面的异步函数创建,这个异步对象在image.encode
完成异步操作后才会被触发。因此这里不需要显式等待encode对象的完成,取而代之则是等待jobs
的完成。所以最后还需要等待所有job的完成。
await Promise.all(jobs);
- 代码清理
await imagePool.close();
用来关掉imagePool
.
相关语法解析
Object
Object.keys()
返回一个对象自己的可枚举的属性名字;
// Simple array
const arr = ["a", "b", "c"];
console.log(Object.keys(arr)); // ['0', '1', '2']
// Array-like object
const obj = { 0: "a", 1: "b", 2: "c" };
console.log(Object.keys(obj)); // ['0', '1', '2']
// Array-like object with random key ordering
const anObj = { 100: "a", 2: "b", 7: "c" };
console.log(Object.keys(anObj)); // ['2', '7', '100']
Object.values()
: 返回对象可枚举的属性的值;
Object.entries()
:返回对象的属性的名字和值;
Object.assign()
: 把源对象的属性拷贝到目标对象中,例如:
const preprocessorOptions = Object.assign(
{},
preprocessors[preprocessorName].defaultOptions,
options,
);
相当于把defaultOptions
和options
拷贝到目标对象preprocessorOptions
中;
map
map是对数组中所有元素都执行相同操作/函数,并生成一个新的数组;例如
const arr = [1, 2, 3];
const syncRes = arr.map((i) => {
return i + 1;
});
console.log(syncRes);
// 2,3,4
map的异步版本是指操作的函数是异步的,返回的对象也是Promise,再使用Promise.all来等待所有的结果都处理完;例如
file_contents = await Promise.all(files.map(async (file) => {
const buffer = await fsp.readFile(file);
return buffer;
}));
这里面分成两步,file.map返回的对象是Promise对象,而Promise.all返回的则是真正的内容;
异步操作await/promise
await
await
用于等待一个Promise
对象实际执行完成,并返回真实操作的结果。例如
const buffer = await fsp.readFile(file)
等待文件操作的实际完成,并把结果保存到buffer中。
Promise.all
await Promise.all
用于同时等待多个Promise对象。
files.map(async (file) => {
console.log("Open file " + file);
const buffer = await fsp.readFile(file);
const image = imagePool.ingestImage(buffer);
await image.decoded;
results.set(image, {
file,
size: (await image.decoded).size,
outputs: [],
});
progress.setProgress(++decoded, files.length);
return image;
}
这一段代码则是把file数组转换成Promise
对象的数组,这个Promise对象实际返回结果是image
类型。这是一种通用的模式async map:
Promise.all(arr.map(async (...) => ...))).
网友评论