- 投稿日:2019-07-08T19:55:56+09:00
おにぎり100円セール?を一瞬で確認できるCLIツールを作りました。
おにぎり100円セール?を一瞬で確認できるツールを作りました。
おにぎり100円セールって魅力的じゃないですか?セブンイレブンでおにぎり100円セールがやっていたら基本的に鮭いくらおにぎりを買うのですが時々初日に間に合わなくて悲しい思いをする事があるのでそんなことをなくす目的で?おにぎり100円セールをCLIから確認できる物作りました。
構成
onigiri-api
とonigiri-cli
の2つの構成で動いています。
onigiri-api
Cloud Function
- クローラー Cloud Schedulerを使用してCloud Functions動かし定期的におにぎり100円セールの更新情報をFirestoreに追加します。
- API 実行されると、Firestoreから値を取得してJSONでレスポンスを返します。
onigiri-cli
Node.jsで、上記のおにぎりAPIから取得したJSONをパースして表示します。
おにぎりCLI
インストールするには
npm
を入れておく必要があります。yarn global add onigiri-cliパスが通っていれば、
onigiri
コマンドで起動します。onigiri実行結果
あまり実行されていないと3秒くらいレスポンスにくらいかかりますねおわり
XPathがあまりわからないので基本的に簡単にかけるようにと、ライブラリを使うのですが、今まではGitHubでスターが多かったのでCheerio - ★というライブラリを使用していたのですが、Cheerioよりjsdom - ★12000↑の方が圧倒的に書きやすかった。(※jsdomはかなり遅いらしい)
おにぎり100円セールが行われていなかったのでおにぎり100円セールを行っているときのHTMLを
web.archive.org
から2つ以上ダウンロードしてきて、パースをする、プログラムを書くことができたがおにぎり100円セールがやっているときに作ったほうがもう少し楽しかったかもしれない。
- 投稿日:2019-07-08T19:55:56+09:00
?おにぎり100円セールを一瞬で確認できるCLIツールを作りました。?
おにぎり100円セール?を一瞬で確認できるツールを作りました。
おにぎり100円セールって魅力的じゃないですか?セブンイレブンでおにぎり100円セールがやっていたら基本的に鮭いくらおにぎりを買うのですが時々初日に間に合わなくて悲しい思いをする事があるのでそんなことをなくす目的で?おにぎり100円セールをCLIから確認できる物作りました。
構成
onigiri-api
とonigiri-cli
の2つの構成で動いています。
onigiri-api
Cloud Function
- クローラー Cloud Schedulerを使用してCloud Functions動かし定期的におにぎり100円セールの更新情報をFirestoreに追加します。
- API 実行されると、Firestoreから値を取得してJSONでレスポンスを返します。
onigiri-cli
Node.jsで、上記のおにぎりAPIから取得したJSONをパースして表示します。
おにぎりCLI
インストールするには
npm
を入れておく必要があります。yarn global add onigiri-cliパスが通っていれば、
onigiri
コマンドで起動します。onigiri実行結果
あまり実行されていないと3秒くらいレスポンスにくらいかかりますねクローラーの実装について
今回のクローラーの実装がそれなりに早く、正確にできたと思うので共有します。
- クローラーを作る際はおにぎり100円セールが現在開催されていなかったので
web.archive.org
から過去のおにぎり100円セールをやっている過去のページ✗2と現在のページの合計3ページを用意 2.ローカルにモックサーバーを立ててページ1 ~ ページ3までアクセスできるようにしておく- おにぎり100円セールをやっている1,2ページが通るテストを書いて実装
- おにぎり100円セールを行っていないページを渡した時のテストを書く
- 3ページ目までテストが通ったら次のコンビニの実装をする.
テストにはJestを使用し、こんな感じなものをJestの公式サイトをみながら、コンビニ各社の実装を繰り返しました。
seveneleven.test.jsimport sevenelevenParser from './seveneleven-parser'; const PAGE_ONE = 'http://127.0.0.1:8080/examples/company/seveneleven/page1.html'; const PAGE_TWO = 'http://127.0.0.1:8080/examples/company/seveneleven/page2.html'; const PAGE_THREE = 'http://127.0.0.1:8080/examples/company/seveneleven/page3.html'; describe('セブンイレブンのパースをする', () => { test('URLを与えないとundefinedが帰ってくる', async () => { const result = await sevenelevenParser(); expect(result).toBe(undefined); }); test('URLを渡すとundefinedが帰ってこない', async () => { const result = await sevenelevenParser(PAGE_ONE); expect(result).not.toBe(undefined); }); test('ページ1を渡すとページタイトルが4日間限定!おにぎり・寿司が税込100円【12月4日(火)まで】とURL、コンビニ名が入ったオブジェクトを返す', async () => { const result = await sevenelevenParser(PAGE_ONE); expect(result).toEqual( expect.objectContaining({ pageTitle: '4日間限定!おにぎり・寿司が税込100円【12月4日(火)まで】', url: 'https://web.archive.org/web/20181202142417/https://www.sej.co.jp/cmp/onigiri1812.html', company: 'seveneleven' }) ); }); test('ページ2を渡すとページタイトルがおにぎり・寿司が税込100円!【7月9日(月)まで】とURL、コンビニ名が入ったオブジェクトが返ってくる', async () => { const result = await sevenelevenParser(PAGE_TWO); expect(result).toEqual( expect.objectContaining({ pageTitle: 'おにぎり・寿司が税込100円!【7月9日(月)まで】', url: 'https://web.archive.org/web/20180707104343/http://www.sej.co.jp/cmp/onigiri1807.html', company: 'seveneleven' }) ); }); test('ページ3を渡すとcompany以外null', async () => { const result = await sevenelevenParser(PAGE_THREE); expect(result).toEqual( expect.objectContaining({ pageTitle: null, url: null, company: 'seveneleven' }) ); }); });おわり
XPathがあまりわからないので基本的に簡単にかけるようにと、ライブラリを使うのですが、今まではGitHubでスターが多かったのでCheerio - ★というライブラリを使用していたのですが、Cheerioよりjsdom - ★12000↑の方が圧倒的に書きやすかった。(※jsdomはかなり遅いらしい)
おにぎり100円セールが行われていなかったのでおにぎり100円セールを行っているときのHTMLを
web.archive.org
から2つ以上ダウンロードしてきて、パースをする、プログラムを書くことができたがおにぎり100円セールがやっているときに作ったほうがもう少し楽しかったかもしれない。おにぎり100円セールはあまり起こらないので誰でもおにぎり100円セールを開催できるようにしたい。
ミニストップは毎日100円セールをやっている
- 投稿日:2019-07-08T19:55:56+09:00
おにぎり100円セールを一瞬で確認できるCLIツールを作りました。?
おにぎり100円セール?を一瞬で確認できるツールを作りました。
おにぎり100円セールって魅力的じゃないですか?セブンイレブンでおにぎり100円セールがやっていたら基本的に鮭いくらおにぎりを買うのですが時々初日に間に合わなくて悲しい思いをする事があるのでそんなことをなくす目的で?おにぎり100円セールをCLIから確認できる物作りました。
構成
onigiri-api
とonigiri-cli
の2つの構成で動いています。
onigiri-api
Cloud Function
- クローラー Cloud Schedulerを使用してCloud Functions動かし定期的におにぎり100円セールの更新情報をFirestoreに追加します。
- API 実行されると、Firestoreから値を取得してJSONでレスポンスを返します。
onigiri-cli
Node.jsで、上記のおにぎりAPIから取得したJSONをパースして表示します。
おにぎりCLI
インストールするには
npm
を入れておく必要があります。yarn global add onigiri-cliパスが通っていれば、
onigiri
コマンドで起動します。onigiri実行結果
あまり実行されていないと3秒くらいレスポンスにくらいかかりますねクローラーの実装について
今回のクローラーの実装がそれなりに早く、正確にできたと思うので共有します。
- クローラーを作る際はおにぎり100円セールが現在開催されていなかったので
web.archive.org
から過去のおにぎり100円セールをやっている過去のページ✗2と現在のページの合計3ページを用意 2.ローカルにモックサーバーを立ててページ1 ~ ページ3までアクセスできるようにしておく- おにぎり100円セールをやっている1,2ページが通るテストを書いて実装
- おにぎり100円セールを行っていないページを渡した時のテストを書く
- 3ページ目までテストが通ったら次のコンビニの実装をする.
テストにはJestを使用し、こんな感じなものをJestの公式サイトをみながら、コンビニ各社の実装を繰り返しました。
seveneleven.test.jsimport sevenelevenParser from './seveneleven-parser'; const PAGE_ONE = 'http://127.0.0.1:8080/examples/company/seveneleven/page1.html'; const PAGE_TWO = 'http://127.0.0.1:8080/examples/company/seveneleven/page2.html'; const PAGE_THREE = 'http://127.0.0.1:8080/examples/company/seveneleven/page3.html'; describe('セブンイレブンのパースをする', () => { test('URLを与えないとundefinedが帰ってくる', async () => { const result = await sevenelevenParser(); expect(result).toBe(undefined); }); test('URLを渡すとundefinedが帰ってこない', async () => { const result = await sevenelevenParser(PAGE_ONE); expect(result).not.toBe(undefined); }); test('ページ1を渡すとページタイトルが4日間限定!おにぎり・寿司が税込100円【12月4日(火)まで】とURL、コンビニ名が入ったオブジェクトを返す', async () => { const result = await sevenelevenParser(PAGE_ONE); expect(result).toEqual( expect.objectContaining({ pageTitle: '4日間限定!おにぎり・寿司が税込100円【12月4日(火)まで】', url: 'https://web.archive.org/web/20181202142417/https://www.sej.co.jp/cmp/onigiri1812.html', company: 'seveneleven' }) ); }); test('ページ2を渡すとページタイトルがおにぎり・寿司が税込100円!【7月9日(月)まで】とURL、コンビニ名が入ったオブジェクトが返ってくる', async () => { const result = await sevenelevenParser(PAGE_TWO); expect(result).toEqual( expect.objectContaining({ pageTitle: 'おにぎり・寿司が税込100円!【7月9日(月)まで】', url: 'https://web.archive.org/web/20180707104343/http://www.sej.co.jp/cmp/onigiri1807.html', company: 'seveneleven' }) ); }); test('ページ3を渡すとcompany以外null', async () => { const result = await sevenelevenParser(PAGE_THREE); expect(result).toEqual( expect.objectContaining({ pageTitle: null, url: null, company: 'seveneleven' }) ); }); });おわり
XPathがあまりわからないので基本的に簡単にかけるようにと、ライブラリを使うのですが、今まではGitHubでスターが多かったのでCheerio - ★というライブラリを使用していたのですが、Cheerioよりjsdom - ★12000↑の方が圧倒的に書きやすかった。(※jsdomはかなり遅いらしい)
おにぎり100円セールが行われていなかったのでおにぎり100円セールを行っているときのHTMLを
web.archive.org
から2つ以上ダウンロードしてきて、パースをする、プログラムを書くことができたがおにぎり100円セールがやっているときに作ったほうがもう少し楽しかったかもしれない。おにぎり100円セールはあまり起こらないので誰でもおにぎり100円セールを開催できるようにしたい。
ミニストップは毎日100円セールをやっている
- 投稿日:2019-07-08T19:55:56+09:00
おにぎり100円セールを一瞬で確認できるonigiriコマンド?
おにぎり100円セール?を一瞬で確認できるツールを作りました。
おにぎり100円セールって魅力的じゃないですか?セブンイレブンでおにぎり100円セールがやっていたら基本的に鮭いくらおにぎりを買うのですが時々初日に間に合わなくて悲しい思いをする事があるのでそんなことをなくす目的で?おにぎり100円セールをCLIから確認できる物作りました。
構成
onigiri-api
とonigiri-cli
の2つの構成で動いています。
onigiri-api
Cloud Function
- クローラー Cloud Schedulerを使用してCloud Functions動かし定期的におにぎり100円セールの更新情報をFirestoreに追加します。
- API 実行されると、Firestoreから値を取得してJSONでレスポンスを返します。
onigiri-cli
Node.jsで、上記のおにぎりAPIから取得したJSONをパースして表示します。
おにぎりCLI
インストールするには
npm
を入れておく必要があります。yarn global add onigiri-cliパスが通っていれば、
onigiri
コマンドで起動します。onigiri実行結果
あまり実行されていないと3秒くらいレスポンスにくらいかかりますねクローラーの実装について
今回のクローラーの実装がそれなりに早く、正確にできたと思うので共有します。
- クローラーを作る際はおにぎり100円セールが現在開催されていなかったので
web.archive.org
から過去のおにぎり100円セールをやっている過去のページ✗2と現在のページの合計3ページを用意 2.ローカルにモックサーバーを立ててページ1 ~ ページ3までアクセスできるようにしておく- おにぎり100円セールをやっている1,2ページが通るテストを書いて実装
- おにぎり100円セールを行っていないページを渡した時のテストを書く
- 3ページ目までテストが通ったら次のコンビニの実装をする.
テストにはJestを使用し、こんな感じなものをJestの公式サイトをみながら、コンビニ各社の実装を繰り返しました。
seveneleven.test.jsimport sevenelevenParser from './seveneleven-parser'; const PAGE_ONE = 'http://127.0.0.1:8080/examples/company/seveneleven/page1.html'; const PAGE_TWO = 'http://127.0.0.1:8080/examples/company/seveneleven/page2.html'; const PAGE_THREE = 'http://127.0.0.1:8080/examples/company/seveneleven/page3.html'; describe('セブンイレブンのパースをする', () => { test('URLを与えないとundefinedが帰ってくる', async () => { const result = await sevenelevenParser(); expect(result).toBe(undefined); }); test('URLを渡すとundefinedが帰ってこない', async () => { const result = await sevenelevenParser(PAGE_ONE); expect(result).not.toBe(undefined); }); test('ページ1を渡すとページタイトルが4日間限定!おにぎり・寿司が税込100円【12月4日(火)まで】とURL、コンビニ名が入ったオブジェクトを返す', async () => { const result = await sevenelevenParser(PAGE_ONE); expect(result).toEqual( expect.objectContaining({ pageTitle: '4日間限定!おにぎり・寿司が税込100円【12月4日(火)まで】', url: 'https://web.archive.org/web/20181202142417/https://www.sej.co.jp/cmp/onigiri1812.html', company: 'seveneleven' }) ); }); test('ページ2を渡すとページタイトルがおにぎり・寿司が税込100円!【7月9日(月)まで】とURL、コンビニ名が入ったオブジェクトが返ってくる', async () => { const result = await sevenelevenParser(PAGE_TWO); expect(result).toEqual( expect.objectContaining({ pageTitle: 'おにぎり・寿司が税込100円!【7月9日(月)まで】', url: 'https://web.archive.org/web/20180707104343/http://www.sej.co.jp/cmp/onigiri1807.html', company: 'seveneleven' }) ); }); test('ページ3を渡すとcompany以外null', async () => { const result = await sevenelevenParser(PAGE_THREE); expect(result).toEqual( expect.objectContaining({ pageTitle: null, url: null, company: 'seveneleven' }) ); }); });おわり
XPathがあまりわからないので基本的に簡単にかけるようにと、ライブラリを使うのですが、今まではGitHubでスターが多かったのでCheerio - ★というライブラリを使用していたのですが、Cheerioよりjsdom - ★12000↑の方が圧倒的に書きやすかった。(※jsdomはかなり遅いらしい)
おにぎり100円セールが行われていなかったのでおにぎり100円セールを行っているときのHTMLを
web.archive.org
から2つ以上ダウンロードしてきて、パースをする、プログラムを書くことができたがおにぎり100円セールがやっているときに作ったほうがもう少し楽しかったかもしれない。おにぎり100円セールはあまり起こらないので誰でもおにぎり100円セールを開催できるようにしたい。
ミニストップは毎日100円セールをやっている
- 投稿日:2019-07-08T19:55:56+09:00
おにぎり100円セールを一瞬で確認できるonigiriコマンドを作った?
おにぎり100円セール?を一瞬で確認できるツールを作りました。
おにぎり100円セールって魅力的じゃないですか?セブンイレブンでおにぎり100円セールがやっていたら基本的に鮭いくらおにぎりを買うのですが時々初日に間に合わなくて悲しい思いをする事があるのでそんなことをなくす目的で?おにぎり100円セールをCLIから確認できる物作りました。
構成
onigiri-api
とonigiri-cli
の2つの構成で動いています。
onigiri-api
Cloud Function
- クローラー Cloud Schedulerを使用してCloud Functions動かし定期的におにぎり100円セールの更新情報をFirestoreに追加します。
- API 実行されると、Firestoreから値を取得してJSONでレスポンスを返します。
onigiri-cli
Node.jsで、上記のおにぎりAPIから取得したJSONをパースして表示します。
おにぎりCLI
インストールするには
npm
を入れておく必要があります。yarn global add onigiri-cliパスが通っていれば、
onigiri
コマンドで起動します。onigiri実行結果
あまり実行されていないと3秒くらいレスポンスにくらいかかりますねおわり
XPathがあまりわからないので基本的に簡単にかけるようにと、ライブラリを使うのですが、今まではGitHubでスターが多かったのでCheerio - ★というライブラリを使用していたのですが、Cheerioよりjsdom - ★12000↑の方が圧倒的に書きやすかった。(※jsdomはかなり遅いらしい)
おにぎり100円セールが行われていなかったのでおにぎり100円セールを行っているときのHTMLを
web.archive.org
から2つ以上ダウンロードしてきて、パースをする、プログラムを書くことができたがおにぎり100円セールがやっているときに作ったほうがもう少し楽しかったかもしれない。おにぎり100円セールはあまり起こらないので誰でもおにぎり100円セールを開催できるようにしたい。
ミニストップは毎日100円セールをやっている
- 投稿日:2019-07-08T19:55:56+09:00
おにぎり100円セールを一瞬で確認できるonigiriコマンドを作りました?
おにぎり100円セール?を一瞬で確認できるツールを作りました。
おにぎり100円セールって魅力的じゃないですか?セブンイレブンでおにぎり100円セールがやっていたら基本的に鮭いくらおにぎりを買うのですが時々初日に間に合わなくて悲しい思いをする事があるのでそんなことをなくす目的で?おにぎり100円セールをCLIから確認できる物作りました。
構成
onigiri-api
とonigiri-cli
の2つの構成で動いています。
onigiri-api
Cloud Function
- クローラー Cloud Schedulerを使用してCloud Functions動かし定期的におにぎり100円セールの更新情報をFirestoreに追加します。
- API 実行されると、Firestoreから値を取得してJSONでレスポンスを返します。
onigiri-cli
Node.jsで、上記のおにぎりAPIから取得したJSONをパースして表示します。
おにぎりCLI
インストールするには
npm
を入れておく必要があります。yarn global add onigiri-cliパスが通っていれば、
onigiri
コマンドで起動します。onigiri実行結果
あまり実行されていないと3秒くらいレスポンスにくらいかかりますねおわり
XPathがあまりわからないので基本的に簡単にかけるようにと、ライブラリを使うのですが、今まではGitHubでスターが多かったのでCheerio - ★というライブラリを使用していたのですが、Cheerioよりjsdom - ★12000↑の方が圧倒的に書きやすかった。(※jsdomはかなり遅いらしい)
おにぎり100円セールが行われていなかったのでおにぎり100円セールを行っているときのHTMLを
web.archive.org
から2つ以上ダウンロードしてきて、パースをする、プログラムを書くことができたがおにぎり100円セールがやっているときに作ったほうがもう少し楽しかったかもしれない。おにぎり100円セールはあまり起こらないので誰でもおにぎり100円セールを開催できるようにしたい。
ミニストップは毎日100円セールをやっている
- 投稿日:2019-07-08T18:27:00+09:00
非同期処理の闇と光
はじめに
今回の記事はNode.jsを使っている際に、非同期処理という便利な機能な弊害となったため、ここに解決策を書き記す。同じく悩む人に伝わってくれ。
非同期処理とは
まずはコードを見てもらおう。ローカルディレクトリのファイル情報を取得するための処理と同時に別の処理を行うと仮定したコードである。
sample.jsconst fs = require('fs') function hogehoge(){ //ファイルを読み取る処理 fs.readFile("<ファイルのパス>", function(error,data){//=処理Aと置く if(error)return -1; //終わったときに表示する console.log("ファイルの読み込みを完了しました!"); console.log("データの中身は:" + data); } } function fugafuga(){//=処理Bとする console.log("fuga~"); } function hogehoge();//ファイルの中身を出す。 function fugafuga();//fuga~と表示この場合に表示されることが以下のときがある。
fuga~ ファイルの読み込みを完了しました! データの中身は:うんたらかんたらなんと、処理の順番が変わっている。
処理A⇒処理Bとなるはずが、処理B⇒処理Aとなっているじゃないか。
これはファイル読み込みに時間がかかることを見越して、「ファイルを読み込む処理をすると同時にほかの処理も行う」という非同期処理がされているからだ。
「うわぁ!なんて便利な機能なんだろう!!!」
そう思うことだろう。
しかし、この機能には実は危険性があるのだ...非同期処理の危険性
sample2.jsconst fs = require('fs') let filestr = ""; function hogehoge(){ //ファイルを読み取る処理 fs.readFile("<ファイルのパス>", function(error,data){ if(error)return -1; filestr = "" + data;//fileの中身をfilestrに移す console.log("ファイルの読み込みを完了しました!"); } } function fugafuga(){ console.log("データの中身は:" + filestr);//filestrの中身を表示 } function hogehoge();//ファイルを読み込んで function fugafuga();//中身を表示こうなった場合どうだろう?勘の鋭い方ならもうお気づきかもしれないが以下の通りになる可能性がある。
データ中身は: ファイルの読み込みを完了しました!「ちょい!ちょい!ちょい!ちょーい!!!」と思うだろう。そう、非同期処理の欠点として、同期処理でなければならない処理も非同期処理になってしまうということである。世に出されている便利な機能が非同期処理になっているというケースが少なくない。ではどう対応すればいいのだろうか。
くらえ!同期処理化アタック!!!
ならば、簡単だ。同期処理に戻してやればいい。
そこでPromiseという機能をつかおう。説明は省くがわかりやすいリンクを乗せておくので、ぜひ参考にしてくれ。
Promiseについて0から勉強してみた出来上がったコードが以下の通りだ。
sample3.jsconst fs = require('fs') let filestr = ""; function hogehoge(){ return new Promise(function(resolve,error){ //ファイルを読み取る処理 fs.readFile("<ファイルのパス>", function(error,data){ if(error)return -1; filestr = "" + data;//fileの中身をfilestrに移す console.log("ファイルの読み込みを完了しました!"); resolve(data)//処理が終わったらresolveでdataを返す。 }); } } function fugafuga(let str){ console.log("データの中身は:" + filestr);//filestrの中身を表示 } function hogehoge()//ファイルを読み込んで .then(function(value){//ファイルが読み込み終わったら fugafuga(value);//中身を表示 }こうすることで、非同期処理を同期処理にしてやることができる。やはり、同期処理が必要とされる場面が多くみられると思うので、Promiseは覚えておいて損はないだろう。
最後に
自分も理解したばかりでまだわからないことも多いので、ぜひ指摘をお願いしたいです。
間違えに気づいた方、改良点に気づいた方はぜひアドバイスをお願いします。
- 投稿日:2019-07-08T18:27:00+09:00
非同期処理の光と闇
はじめに
今回の記事はNode.jsを使っている際に、非同期処理という便利な機能な弊害となったため、ここに解決策を書き記す。同じく悩む人に伝わってくれ。
非同期処理とは
まずはコードを見てもらおう。ローカルディレクトリのファイル情報を取得するための処理と同時に別の処理を行うと仮定したコードである。
sample.jsconst fs = require('fs') function hogehoge(){ //ファイルを読み取る処理 fs.readFile("<ファイルのパス>", function(error,data){//=処理Aと置く if(error)return -1; //終わったときに表示する console.log("ファイルの読み込みを完了しました!"); console.log("データの中身は:" + data); } } function fugafuga(){//=処理Bとする console.log("fuga~"); } function hogehoge();//ファイルの中身を出す。 function fugafuga();//fuga~と表示この場合に表示されることが以下のときがある。
fuga~ ファイルの読み込みを完了しました! データの中身は:うんたらかんたらなんと、処理の順番が変わっている。
処理A⇒処理Bとなるはずが、処理B⇒処理Aとなっているじゃないか。
これはファイル読み込みに時間がかかることを見越して、「ファイルを読み込む処理をすると同時にほかの処理も行う」という非同期処理がされているからだ。
「うわぁ!なんて便利な機能なんだろう!!!」
そう思うことだろう。
しかし、この機能には実は危険性があるのだ...非同期処理の危険性
sample2.jsconst fs = require('fs') let filestr = ""; function hogehoge(){ //ファイルを読み取る処理 fs.readFile("<ファイルのパス>", function(error,data){ if(error)return -1; filestr = "" + data;//fileの中身をfilestrに移す console.log("ファイルの読み込みを完了しました!"); } } function fugafuga(){ console.log("データの中身は:" + filestr);//filestrの中身を表示 } function hogehoge();//ファイルを読み込んで function fugafuga();//中身を表示こうなった場合どうだろう?勘の鋭い方ならもうお気づきかもしれないが以下の通りになる可能性がある。
データ中身は: ファイルの読み込みを完了しました!「ちょい!ちょい!ちょい!ちょーい!!!」と思うだろう。そう、非同期処理の欠点として、同期処理でなければならない処理も非同期処理になってしまうということである。世に出されている便利な機能が非同期処理になっているというケースが少なくない。ではどう対応すればいいのだろうか。
くらえ!同期処理化アタック!!!
ならば、簡単だ。同期処理に戻してやればいい。
そこでPromiseという機能をつかおう。説明は省くがわかりやすいリンクを乗せておくので、ぜひ参考にしてくれ。
Promiseについて0から勉強してみた出来上がったコードが以下の通りだ。
sample3.jsconst fs = require('fs') let filestr = ""; function hogehoge(){ return new Promise(function(resolve,error){ //ファイルを読み取る処理 fs.readFile("<ファイルのパス>", function(error,data){ if(error)return -1; filestr = "" + data;//fileの中身をfilestrに移す console.log("ファイルの読み込みを完了しました!"); resolve(data)//処理が終わったらresolveでdataを返す。 }); } } function fugafuga(let str){ console.log("データの中身は:" + filestr);//filestrの中身を表示 } function hogehoge()//ファイルを読み込んで .then(function(value){//ファイルが読み込み終わったら fugafuga(value);//中身を表示 }こうすることで、非同期処理を同期処理にしてやることができる。やはり、同期処理が必要とされる場面が多くみられると思うので、Promiseは覚えておいて損はないだろう。
最後に
自分も理解したばかりでまだわからないことも多いので、ぜひ指摘をお願いしたいです。
間違えに気づいた方、改良点に気づいた方はぜひアドバイスをお願いします。
- 投稿日:2019-07-08T16:05:11+09:00
コーディング規約
JavaScript本格入門(ISBN 978-4774184111)で基礎からJavaScriptを勉強するシリーズです。今回はChapter8よりコーディング規約についてです。
概要
複数人で開発していると、コーディング規約が大切ということについては言わずもがなだと思います。
JavaScriptのコーディング規約については、下記の記事に、有名どころが纏まっていますので参考にさせていただきました。筆者がNode.jsで開発することが多いので、Felix's Node.js Style Guideのコーディング規約を使って、書いたコードをチェックするようにしてみます。
環境
記事作成時点の環境は以下の通りです。
- Windows 10 Home edition(64bit)
- Node.js 10.15.1
- npm 6.4.1
ESlintのインストール
ESlint(リンター)を使ってコーディング規約に則っているかをチェックしますので入れます。
インストール$ npm install eslintバージョン確認$ eslint --version v6.0.1実践
コーディングしてみる
何も考えずに、テキトーに動くサンプルを作ってみました。
さてチェックするとどうなるでしょうか。main.jsvar Main = function(arg1, arg2) { console.log("Your input is " + arg1 + ".") } Main("hoge");Felix's Node.js Style Guideの設定を持ってくる
ESlintは
.eslintrc.json
ファイルをESlintの設定として読み込んでくれます。今回はFelix's Node.js Style Guideを利用するのでGitHubから持ってきます。
(ほんとはgit clone & バージョン指定して持ってくるべき)参照:https://github.com/felixge/node-style-guide/blob/master/.eslintrc
.eslintrc.json{ "env": { "node": true }, "rules": { "array-bracket-spacing": [2, "never"], "block-scoped-var": 2, "brace-style": [2, "1tbs"], "camelcase": 1, "computed-property-spacing": [2, "never"], "curly": 2, "eol-last": 2, "eqeqeq": [2, "smart"], "max-depth": [1, 3], "max-len": [1, 80], "max-statements": [1, 15], "new-cap": 1, "no-extend-native": 2, "no-mixed-spaces-and-tabs": 2, "no-trailing-spaces": 2, "no-unused-vars": 1, "no-use-before-define": [2, "nofunc"], "object-curly-spacing": [2, "never"], "quotes": [2, "single", "avoid-escape"], "semi": [2, "always"], "keyword-spacing": [2, {"before": true, "after": true}], "space-unary-ops": 2 } }ESlintでチェックしてみる
$ eslint main.js 1:27 warning 'arg2' is defined but never used no-unused-vars 2:17 error Strings must use singlequote quotes 2:43 error Strings must use singlequote quotes 2:47 error Missing semicolon semi 3:2 error Missing semicolon semi 5:1 warning A function with a name starting with an uppercase letter should only be used as a constructor new-cap 5:6 error Strings must use singlequote quotes 5:14 error Newline required at end of file but not found eol-last ✖ 8 problems (6 errors, 2 warnings) 6 errors and 0 warnings potentially fixable with the `--fix` option.いっぱい怒られました!直しましょう!
コードを直す
fixedvar main = function(arg1) { console.log('Your input is ' + arg1 + '.'); }; main('hoge');警告に従って直しました。
最後にもう一度チェック
$ eslint main.js何も言われませんでした。これで安心してプルリクエストできます。
まとめ
- 投稿日:2019-07-08T14:42:41+09:00
JavaScriptの同時実行モデルについて
概要
JavaScriptが多くの言語と異なる点のひとつに、イベントループベースの同時実行モデルがあります。これは、JavaScript自体というよりも、ブラウザの仕組みにも関わりがあります。
今回はそれについて書いてみようと思います。
ラインタイム
MDNの図を拝借すると、JavaScriptのラインタイムは以下のような要素で構成されています。ひとつずつ説明してゆきます。
ヒープ
ヒープはヒープですね。今回のテーマとは少し外れるので省略します。
スタック
JavaScriptで処理を呼び出すと、コールスタックが積み上がります。
JavaScriptはシングルスレッドなので、一度にひとつの処理しか実行できません。例えば、以下のように関数を呼び出すような処理を考えます。
function subA() { console.log('A'); } function subB() { console.log('B'); } function main() { subA(); subB(); } main();これは、以下のようなスタックを形成します。
main
のフレームmain
から呼び出されたsubA
のフレームsubA
の実行が完了するとsubA
のフレームはスタックからポップアウトmain
から呼び出されたsubB
のフレームsubB
の実行が完了するとsubB
のフレームはスタックからポップアウト- すべての実行が完了すると
main
のフレームはスタックからポップアウト上記のように同期的な関数実行では、処理がひとつずつ行われてゆくことがわかります。
キュー
JavaScriptにおけるキューとは、次に実行すべき処理の一覧といえます。
ここでは
setTimeout
を使用した以下のコードを考えてみます。function setTimeoutCallback() { console.log('A'); } function subA() { setTimeout(setTimeoutCallback, 7000) } function subB() { console.log('B'); } function main() { subA(); subB(); } main();これは、以下のようなスタックとキューを形成します。
main
のフレームmain
から呼び出されたsubA
のフレームsubA
から呼び出されたsetTimeout
のフレームsetTimeout
の実行が完了するとsetTimeout
のフレームはスタックからポップアウト。ブラウザ側でタイマーの計算開始。main
から呼び出されたsubB
のフレームsubB
の実行が完了するとsubB
のフレームはスタックからポップアウト4
のタイマーが終了すると、ブラウザがイベントをキューに追加する- キューに追加された処理をイベントループで検知、処理を実行する
- すべての実行が完了すると
main
のフレームはスタックからポップアウト
4
でタイマーの処理がJavaScriptのメインスレッドを離れており、キューの仕組みがあることでノンブロッキングに処理が進んでいます。さらに、
setTimeout
を連続で呼び出す場合も考えてみます。function setTimeoutCallback() { console.log('A'); } function subA() { setTimeout(setTimeoutCallback, 7000) setTimeout(setTimeoutCallback, 7000) setTimeout(setTimeoutCallback, 7000) setTimeout(setTimeoutCallback, 7000) setTimeout(setTimeoutCallback, 7000) } function subB() { console.log('B'); } function main() { subA(); subB(); } main();これは、以下のようなスタックとキューを形成します。
上記のように、呼び出し自体は関数ひとつずつですが、処理自体はブラウザAPIによって同時実行され、完了したものからJavaScript側のキューにたまってゆくという仕組みです。
これはスレッドなどで実現できる
並列(parallel)
処理とはいえませんが、実際の処理をJavaScriptランタイムの外にだすことで、並行(concurrent)
に処理を実行していると言えます。(
並行
/並列
/非同期
/ノンブロッキング
あたりは勘違いされやすいので、違いをおさえておくといいと思います)。イベントループ
上記でも言及した、キューを検知できる仕組みをイベントループと呼びます。イベントループは、イベントを待機する実装のひとつで、MDNの例を借りるなら、以下のような実装に似ています。
while(queue.waitForMessage()){ queue.processNextMessage(); }シングルスレッドでのイベントループは、同時多発的に発生するイベントを、軽量に扱うのに向いています。これはプロセスが大量に立ち上がり、メモリやコンテキストスイッチで処理が重くなるのを防ぐことができるからです。(ただし、大量の計算など時間のかかる処理は苦手で、処理が終わるまで後続処理をブロックしてしまいます)。
JavaScript以外でシングルスレッドのイベント駆動アーキテクチャを採用しているものとしては、nginxが有名です。
nginxは、上記のような方針でC10K問題を解決しようとしていて、Node.jsもその文脈で話されているのをたまにみかけます。しかし、もともとJavaScriptは、90年代のウェブブラウザという、十分なリソースがない環境のために開発された言語です。限られたCPUで少しでもまともに動くように実装されたのがシングルスレッド+イベントループというアーキテクチャだった、というのが正しい背景かと思います。
つまり、イベントループのアーキテクチャは、ブラウザで発生する大量のイベントを処理するのに(少なくとも当時は)最適なユースケースであったと考えることができます。現在では、Goのように軽量スレッドを使用した方が線がいい場合も多いかもしれません。
応用例
ここで少し実際に役に立ちそうな例を見てみます。
有名な例ではありますが、
setTimeout(func, 0)
を使って処理をキューに外出しし、描画タイミングを早くするという手法があります。例えば、以下のように、DOMの変更処理のあとに、かなり時間のかかる処理があったとします。// DOMの変更処理 var target = document.getElementById('target'); target.textContent = 'updated'; // 時間のかかる処理 var calcResult; function longTask() { for (var i = 0; i < 1000000000; i++) { calcResult = i * 3; } } longTask();このようなコードを書くと、実際の画面変更はすぐには行われません。ブラウザで描画の処理が走る前に、10億回のループでスレッドが埋まってしまうので、描画が後回しにされてしまいます。メインスレッドを見ると
longTask
に約4秒かかっているので、その分ユーザーはインタラクションを待つことになるでしょう。描画を早くする方法のひとつとして、以下のように「
setTimeout(func, 0)
で処理を一旦キューに押し込める」などがあります。// DOMの変更処理 var target = document.getElementById('target'); target.textContent = 'updated'; // 時間のかかる処理 var calcResult; window.setTimeout(function longTask() { for (var i = 0; i < 1000000000; i++) { calcResult = i * 3; } }, 0)こうすることで、描画処理を先に走らせることができるため、パフォーマンスを改善することができます。
ただし、後続の処理をブロックしてしまうことには変わりないので、処理中の表示にしたり、Workerを使ったりするなども検討したほうがよさそうです。いずれにせよ、仕組みを理解しておくことは思わぬ事故へのリスクヘッジにもなると考えます。
まとめ
今回はJavaScriptの同時実行モデルについて紹介しました。以下の言葉を聞いてスッとイメージができればこの記事のまとめになっているかなと思います。
- スタック、キュー
- シングルスレッド、イベントループ
- 並列、並行
お役に立てれば幸いです。
参考
- 投稿日:2019-07-08T09:09:29+09:00
TypeScriptによるバックエンドとフロントエンドの両面開発
TypeScriptによるバックエンドとフロントエンドの両面開発
※全てをTypeScriptで記述したツリー型情報掲載システムを運用中です
1.言語の選択
Web開発を一つの言語で行おうと思った場合、事実上の選択肢は素のJavaScriptを使うか、AltScript系を使うことになります。
前者のJavaScriptは気軽にコーディングできる反面、気付かぬうちに危険なコードが混入します。JavaScriptは仕様をきっちり把握していればある程度回避できますが、実は地雷原をサンダルで散歩するに等しい言語なのです。今日は無事でも、明日は足が吹っ飛ばされるかもしれません。
JavaScriptの危険性を回避するにはAltScript、中でも急速に伸びているTypeScriptを強くお勧めします。TypeScriptが一つ使えれば、フロントエンドとバックエンド両方が同じ言語で、しかもある程度の安全を担保した状態で開発出来るのです。
2.TypeScriptによる両面開発の利点
同じ文法なので効率が上がる
例えばバックエンドをPHP、フロントエンドをJavaScriptで開発したケースです。この二つの言語、文法が似ています。似ているが故に同時開発すると、拡張for文などでミスります。PHPでfor-inを使おうとしてしまったりと、一瞬で気が付くミスですが、けっこうやってしまいがちです。完全に同じ文法の言語を使えば、こういった部分でのストレスがありません。ソースコードが使いまわせる
フロントエンドとバックエンドで同じような処理を書く場合があります。言語が違えばいちいち書き直しですが、同じ言語ならコピペで終了です。共通ライブラリとして整備しておけば、コピペの必要すらありません。データのやり取りが楽
TypeScriptなら型が組めるわけですが、同じ言語ならこの型が共通化できます。特に威力を発揮するのは通信部分です。バックエンドとフロントエンドの通信は、Ajaxを用いた非同期通信を用います。やりとりの標準的な方法では、いったんJSON形式を通してパースし、お互いの終端でデコードして使います。TypeScriptなら、このデコード後の型が共通化できるのです。これが出来るか否かで、開発効率がまったく変わってきます。感覚的にはローカルファンクションを呼び出す感覚で通信が可能となるのです。3.両面開発で気を付けるべきこと
3.1 開発環境の設定
VSCodeなどの開発環境は、TypeScriptの文法チェックをするときにtsconfig.jsonを参照します。この設定値を元にチェック項目を判断するわけです。当然ですがバックエンドとフロントエンドでこの設定値が異なります。特に入出力ディレクトリは確実に異なった設定になるので、きっちり区別をつけておかないと、大量のエラーに悩まされることになります。
まず最初にやらないといけないのは、プロジェクトルートのディレクトリに配置するtsconfig.jsonです。
tsconfig.json{ "exclude": [ "." ] }これを置いておかないと、全てのディレクトリの.tsが混合でチェックされてしまうので悲惨なことになります。これを配置したら、あとはフロントエンド用とバックエンド用のディレクトリそれぞれにtsconfig.jsonを作ればOKです。コンパイル時、手動でそれぞれコンパイルするなら
tsc -b configファイルのパス
とします。
また、異なるtsconfig.jsonをトップのtsconfig.jsonから呼び出すこともできます。
tsconfig.json{ "references": [ { "path": "フロントエンドパス" },{ "path": "バックエンドパス" } ], "exclude": [ "." ] }としておけば、
tsc -b
だけで、両方同時にビルドすることができます。ただし、フロントエンドはWebPackなどのモジュールバンドラを使うケースが多いのでその場合、ここに記述するのはバックエンドの参照のみになります。また、参照を受け付ける場合は、参照先の設定に
tsconfig.json{ "compilerOptions": { "composite": true } }を入れておく必要があります。
3.2 出来ることの違い
バックエンドはファイルの入出力やDBアクセスなど、一通り何でもできます。ただしDOM操作などは標準対応しておらず、自分で組むか、jsdomのようなパッケージをnpmからとってくる必要があります。
フロントエンドはDOM操作が標準でできますが、当然ファイル操作などは出来ません。ブラウザ内で許可されているlocalstrageを使ったり、バックエンドに入出力要求する必要があります。
ということで使用可能なAPIはまったく異なるので、ここだけはきちんと区別して開発しなければなりません。
4.最終的な開発環境とビルド手順
現在私の開発方法はフロントエンドにWebPack、バックエンドにtscという形で行っています。tscの方は参照設定で複数のプロジェクトを一コマンドでコンパイル出来るのですが、WebPackにはプロジェクトの参照設定がないので、ちょっと面倒くさいことになっています。
- tsc - バックエンドメインプロジェクト - Active-Module-Framework
- WebPack - フロントエンドメインプロジェクト
- WebPack - JavaScript-Window-Framework
バックエンドはActive-Module-Frameworkというオレオレフレームワークを使っており、バックエンドのメインプロジェクトとは独立させた構成になっています。tscは参照設定を使うことによって、この異なるプロジェクトの同時ビルドが可能です。
フロントエンドはJavaScript-Window-Frameworkというオレオレフレームワークを使っており、フロントエンドのメインプロジェクトとは独立させた構成になっています。WebPackには参照設定がないので、フロントエンドのビルドには、WebPackを二回立ち上げます。-wオプションを入れて監視ビルドさせるので、最初にそれぞれ一回起動する手間があるだけなのですが、やっぱりちょっと面倒くさいです。
5.利点は大きいが移行するまでの壁も大きい
TypeScriptによる両面開発は数々のメリットがある反面、壁が大きいのも確かです。まずNode.jsの非同期処理に慣れるまでそれなりに時間がかかります。これは素のJavaScriptでも同じですが、他の言語には無い特徴故に慣れるまでに時間を要します。さらにTypeScriptの型の扱いも、ケースごとにどう対処するのかという知識を蓄積するまでに、多少の時間をとられます。この壁を乗り越えた先にたどり着けるかどうかは、各々のやり方次第です。しかしWeb開発という面に限って言えば、とても大きな効率化を図ることが出来るのです。
- 投稿日:2019-07-08T07:07:32+09:00
firebase serveで注意されたときのメモ node のバージョン変更
firebase serve
で注意されたときのメモYour requested "node" version "8" doesn't match your global version "12"
nodebrew で使用可能なnodeのバージョンを確認
$ nodebrew ls-remote v8.0.0 v8.1.0 v8.1.1 v8.1.2 v8.1.3 v8.1.4 v8.2.0 v8.2.1 v8.3.0 v8.4.0 v8.5.0 v8.6.0 v8.7.0 v8.8.0 v8.8.1 v8.9.0 v8.9.1 v8.9.2 v8.9.3 v8.9.4 v8.10.0 v8.11.0 v8.11.1 v8.11.2 v8.11.3 v8.11.4 v8.12.0 v8.13.0 v8.14.0 v8.14.1 v8.15.0 v8.15.1 v8.16.0v8.16.0 をインストールします
$ nodebrew install-binary v8.16.0現在インストールされている node を確認
$ nodebrew ls v8.16.0 v10.11.0 current: v10.11.0インストールされているものから使いたいバージョンを選ぶ
$ nodebrew use v8.16.0 use v8.16.0node のバージョンを確かめる
$ node -v v12.5.0まだ v12となっているのでpathを通します
$ source ~/.bash_profile確認します
$ node -v v8.16.0
- 投稿日:2019-07-08T02:52:13+09:00
NetlifyのCIでNode.jsのバージョンを最新に指定
Nuxt.jsをSSRしてNetlifyに上げて運用しています。
ローカル環境はNode.js v12を利用していますが、なかなかサンプルコードなども見当たらなかったのでメモ
[途中] Netlify ドキュメント 日本語翻訳が参考になりました。
.nvmrcを追加するだけ
node -v > .nvmrcという記述がありますが。
プロジェクトルートに
.nvmrc
というファイルを作成し、中にバージョンを記述すればおkです。v12.6.0これでNetlifyにデプロイすればimageにインストールされて使えます。
対応バージョンは?
ログを見る限りだとnvmで利用できるバージョンがそのまま利用できそうなので、最新を常に使うことができそうです。ありがたいですね。
こんな感じで裏側でインストールされてました。なので初回は少し時間がかかる印象ですが、2回目以降はこんな感じで既にインストールされた状態になっているのでビルド時間も特段気にしなくて良さそうです。