Squoosh-cli的代碼解析

cli是libsquoosh的命令行工具盹靴,可以直接在命令行中直接運(yùn)行掀宋。

Cli的核心流程分析

cli使用commander包來(lái)實(shí)現(xiàn)命令行界面钉鸯,核心函數(shù)是processFiles,執(zhí)行的關(guān)鍵流程是

  1. 讀文件
      const buffer = await fsp.readFile(file);
  1. 解碼得到image 的原始data鹅巍,也就是未壓縮的數(shù)據(jù):
      const image = imagePool.ingestImage(buffer);
      await image.decoded;

ingestImage會(huì)創(chuàng)建一個(gè)新的Image對(duì)象音同,該對(duì)象的構(gòu)造函數(shù)會(huì)通過(guò)WorkerPooldispatchJob post 一個(gè)decode的message到WorkerPooljobQueue中词爬,WorkerPool則pick 一個(gè)worker,并用這個(gè)worker來(lái)做實(shí)際的解碼权均;dispatchJob是一個(gè)異步操作顿膨, 內(nèi)部會(huì)創(chuàng)建一個(gè)新的Promise對(duì)象,該對(duì)象會(huì)在圖像解碼的完成的時(shí)候叽赊,把解碼得到的buffer作為返回值返回恋沃,也即該對(duì)象的實(shí)際結(jié)果。
await image.decoded就是等待解碼完成必指。

  1. 解析preprocessors的選項(xiàng):
  const preprocessOptions = {};
  for (const preprocessorName of Object.keys(preprocessors)) {
    if (!program.opts()[preprocessorName]) {
      continue;
    }
    preprocessOptions[preprocessorName] = JSON5.parse(
      program.opts()[preprocessorName],
    );
  }

這部分主要是檢查是否有preprocessors的相關(guān)參數(shù)囊咏,有的話就構(gòu)造一個(gè)option.

  1. 調(diào)用preprocess
  for (const image of decodedFiles) {
    image.preprocess(preprocessOptions);
  }
  await Promise.all(decodedFiles.map((image) => image.decoded));

注意image.preprocess是一個(gè)async函數(shù),但他本身的結(jié)果或者返回值本身并不重要塔橡。它內(nèi)部調(diào)用this.workerPool.dispatchJob讓worker_pool來(lái)做實(shí)際的preprocess操作梅割,并把對(duì)應(yīng)的Promise賦值給image.decoded。因此在調(diào)用preprocess之后谱邪,還需要再調(diào)用await Promise.all來(lái)等待所有image.decoded的完成炮捧。

  1. 調(diào)用實(shí)際的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是一個(gè)Promise對(duì)象庶诡,它是由then()里面的異步函數(shù)創(chuàng)建惦银,這個(gè)異步對(duì)象在image.encode完成異步操作后才會(huì)被觸發(fā)。因此這里不需要顯式等待encode對(duì)象的完成末誓,取而代之則是等待jobs的完成扯俱。所以最后還需要等待所有job的完成。

await Promise.all(jobs);
  1. 代碼清理
await imagePool.close();

用來(lái)關(guān)掉imagePool.

相關(guān)語(yǔ)法解析

Object

Object.keys() 返回一個(gè)對(duì)象自己的可枚舉的屬性名字喇澡;

// 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(): 返回對(duì)象可枚舉的屬性的值迅栅;
Object.entries():返回對(duì)象的屬性的名字和值;
Object.assign(): 把源對(duì)象的屬性拷貝到目標(biāo)對(duì)象中晴玖,例如:

      const preprocessorOptions = Object.assign(
        {},
        preprocessors[preprocessorName].defaultOptions,
        options,
      );

相當(dāng)于把defaultOptionsoptions拷貝到目標(biāo)對(duì)象preprocessorOptions中读存;

map

map是對(duì)數(shù)組中所有元素都執(zhí)行相同操作/函數(shù)为流,并生成一個(gè)新的數(shù)組;例如

const arr = [1, 2, 3];

const syncRes = arr.map((i) => {
    return i + 1;
});

console.log(syncRes);
// 2,3,4

map的異步版本是指操作的函數(shù)是異步的让簿,返回的對(duì)象也是Promise敬察,再使用Promise.all來(lái)等待所有的結(jié)果都處理完;例如

file_contents = await Promise.all(files.map(async (file) => {
  const buffer = await fsp.readFile(file);
  return buffer;
}));

這里面分成兩步尔当,file.map返回的對(duì)象是Promise對(duì)象莲祸,而Promise.all返回的則是真正的內(nèi)容;

異步操作await/promise

await

await用于等待一個(gè)Promise對(duì)象實(shí)際執(zhí)行完成椭迎,并返回真實(shí)操作的結(jié)果锐帜。例如
const buffer = await fsp.readFile(file)等待文件操作的實(shí)際完成,并把結(jié)果保存到buffer中畜号。

Promise.all

await Promise.all用于同時(shí)等待多個(gè)Promise對(duì)象缴阎。

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數(shù)組轉(zhuǎn)換成Promise對(duì)象的數(shù)組,這個(gè)Promise對(duì)象實(shí)際返回結(jié)果是image類(lèi)型简软。這是一種通用的模式async map:
Promise.all(arr.map(async (...) => ...))).

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末药蜻,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子替饿,更是在濱河造成了極大的恐慌语泽,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,817評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件视卢,死亡現(xiàn)場(chǎng)離奇詭異踱卵,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)据过,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)惋砂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人绳锅,你說(shuō)我怎么就攤上這事西饵。” “怎么了鳞芙?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,354評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵眷柔,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我原朝,道長(zhǎng)驯嘱,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,498評(píng)論 1 284
  • 正文 為了忘掉前任喳坠,我火速辦了婚禮鞠评,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘壕鹉。我一直安慰自己剃幌,他們只是感情好聋涨,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,600評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著负乡,像睡著了一般牛郑。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上敬鬓,一...
    開(kāi)封第一講書(shū)人閱讀 49,829評(píng)論 1 290
  • 那天淹朋,我揣著相機(jī)與錄音,去河邊找鬼钉答。 笑死础芍,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的数尿。 我是一名探鬼主播仑性,決...
    沈念sama閱讀 38,979評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼右蹦!你這毒婦竟也來(lái)了诊杆?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,722評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤何陆,失蹤者是張志新(化名)和其女友劉穎晨汹,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體贷盲,經(jīng)...
    沈念sama閱讀 44,189評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡淘这,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,519評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了巩剖。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片铝穷。...
    茶點(diǎn)故事閱讀 38,654評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖佳魔,靈堂內(nèi)的尸體忽然破棺而出曙聂,到底是詐尸還是另有隱情,我是刑警寧澤鞠鲜,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布宁脊,位于F島的核電站,受9級(jí)特大地震影響镊尺,放射性物質(zhì)發(fā)生泄漏朦佩。R本人自食惡果不足惜并思,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,940評(píng)論 3 313
  • 文/蒙蒙 一庐氮、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧宋彼,春花似錦弄砍、人聲如沸仙畦。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,762評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)慨畸。三九已至,卻和暖如春衣式,著一層夾襖步出監(jiān)牢的瞬間寸士,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,993評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工碴卧, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留弱卡,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,382評(píng)論 2 360
  • 正文 我出身青樓住册,卻偏偏與公主長(zhǎng)得像婶博,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子荧飞,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,543評(píng)論 2 349

推薦閱讀更多精彩內(nèi)容