質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Node.js

Node.jsとはGoogleのV8 JavaScriptエンジンを使用しているサーバーサイドのイベント駆動型プログラムです。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

解決済

1回答

2117閲覧

"zipファイルを解凍する"をawaitしたい

退会済みユーザー

退会済みユーザー

総合スコア0

Node.js

Node.jsとはGoogleのV8 JavaScriptエンジンを使用しているサーバーサイドのイベント駆動型プログラムです。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

0グッド

0クリップ

投稿2019/05/23 06:23

プロミス初心者です。
ローカルにあるzipフォルダを解凍してから、展開されたフォルダを読み込みたいです。
それをするために、zipフォルダを解凍するをawaitしてから展開されたフォルダを読み込みたく思い、
awatiできるunzip関数を作りました。

使用するライブラリは以下です。

bluebird
https://www.npmjs.com/package/bluebird

unzipper
https://www.npmjs.com/package/unzipper

fs-extra
https://www.npmjs.com/package/fs-extra

js

1// unzip module 2 3import fs from 'fs-extra' 4import unzipper from 'unzipper' 5import Promise from 'bluebird' 6 7export default ({ inputDirPath, outputFilePath }) => { 8 return new Promise(async (resolve, reject, onCancel) => { 9 try { 10 await streamPromise(fs.createReadStream(inputDirPath).pipe(unzipper.Extract({ path: outputFilePath }))) 11 resolve() 12 } catch (error) { 13 reject(error) 14 } 15 }) 16} 17 18const streamPromise = (stream) => { 19 return new Promise((resolve, reject) => { 20 stream.on('end', () => { 21 resolve('end') 22 }) 23 stream.on('finish', () => { 24 resolve('finish') 25 }) 26 stream.on('error', (error) => { 27 reject(error) 28 }) 29 }) 30}

最初は

js

1export default ({ inputDirPath, outputFilePath }) => { 2 return new Promise(async (resolve, reject, onCancel) => { 3 try { 4 await fs.createReadStream(inputDirPath).pipe(unzipper.Extract({ path: outputFilePath })) 5 resolve() 6 } catch (error) { 7 reject(error) 8 } 9 }) 10}

このように書いてしまって、fs.createReadStreamの返り値はPromiseじゃなくてstream?だからawaitができず、resolveがfs.createReadStreamを追い越してしまっていました...。

そこで、

https://github.com/jprichardson/node-fs-extra/issues/433

こちらの記事を参考にしてみたら、1つ目のコードでうまくいきました。
しかし、streamPromise関数のことなど、全然理解できてません。
またストリームというものもよく理解できてません。
これは一体どういうことなのでしょうか。
なぜawaitができるのでしょうか。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

そもそもawaitは普通promiseに対して使うもので、promiseがresolveするまで待ち、その値を取り出すというものです。一応promiseについて説明すると、promiseは非同期処理を行いやすくするためのものであり、処理が完了するとresolve、失敗するとrejectすることが期待されています。

streamはpromiseではないため変換する必要があり、それをしているのがstreamPromiseです。new Promise()でpromiseを作る方法が分からない場合は適当に検索してください。例えばこれです。

Promise再入門① Promise基本編 - Qiita

今の場合zipファイルの書き込みが終了したタイミングでresolveしたいわけですが、streamPromiseの引数となっているfs.createReadStream(inputDirPath).pipe(unzipper.Extract({ path: outputFilePath }))について少し考える必要があります。

streamは流れるデータを扱うためのものですが、writableとreadableの2種類あります。writableは流れるデータを受け取るもの、readableはデータを発生させるものです。今の場合感覚的に言うとfs.createReadStream(inputDirPath)で作られたreadble stream(Aとする)はファイルの内容を送り出し、unzipper.Extract({ path: outputFilePath })は受け取ったデータをunzipしてファイルに書き込むwritable stream(Bとする)です。これらを.pipeメソッドでつなげることで、ファイルをunzipしてファイルに書き込む、というのを実現しています。.pipe()の返り値はその引数そのままなので、streamPromiseが受け取るのはBとなります。

Aのデータが尽きてendイベントが発生すると、pipeされたBでいずれfinishイベントが発生する(ことが期待される)ので、このタイミングでresolveすればいいことになります。同様に、エラーが発生するとerrorイベントが発生するのでこのときにrejectすればいいです。promiseと異なりstreamは.onでイベントリスナーを設定するので、質問文にあるコードのようになります。

endイベントもリッスンしている理由は、writable streamでもreadable streamでもstreamPromiseを使えるようにするためだと思われます。

投稿2019/05/23 10:17

編集2019/05/23 10:25
karamarimo

総合スコア2551

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2019/05/24 00:01

とても丁寧な回答ありがとうございます。 なるほどです、 https://qiita.com/tfrcm/items/1dfe4265c36bea903ab3 こちらの記事にあるように、セットタイムアウトのなかでリゾルブを読んでいるのが、今回で言う stream.onの中でリゾルブを呼ぶに当たるのですね…
退会済みユーザー

退会済みユーザー

2019/05/24 00:32

あと、ストリームプロミス関数をawaitしてからresolveじゃなくて、 streamPromise().then(resolve()) という書き方も多分できますよね
karamarimo

2019/05/24 06:43 編集

streamPromise().then(resolve) ですね。 もっというと new Promise( ) 自体必要ありません。下のようにしても、resolveの値が変わる以外の違いはありません。 export default ({ inputDirPath, outputFilePath }) => { return streamPromise(fs.createReadStream(inputDirPath).pipe(unzipper.Extract({ path: outputFilePath }))) }
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問