HerokuにLaravel+MySQL環境をデプロイする
heroku上にlaravel環境を構築したのでメモ。
前提条件
作業は下記をインストール済の環境で行なっている。また、事前にHerokuへの登録を済ましておく必要がある。
laravelプロジェクトの作成
以下コマンドでlaravelプロジェクトを作成する。myApp
には任意のディレクトリ名を指定する。
$ composer create-project laravel/laravel myApp
herokuアプリの作成
作成したディレクトリに移動し、herokuアプリを作成する。myApp
には任意のアプリ名を指定する。
$ cd myApp $ heroku login $ heroku create myApp --buildpack heroku/php
これでheroku上にアプリとgitリポジトリが作成される。
ex) https://myApp.herokuapp.com/ | https://git.heroku.com/myApp.git
gitの登録
herokuのgitリモートリポジトリが利用できるようになったので、ローカルにもgitを作成する。
$ git init $ heroku git:remote -a myApp // リモートリポジトリを登録
これでHeroku上のgitリポジトリにソースコードをpushできるようになる。 laravelプロジェクトをpushしておく。
$ git add -A . $ git commit -m "first commit" $ git push heroku master
clearDBの作成
続いてデータベースを利用できるようにする。
herokuに用意されているMySQL互換のclearDB
アドオンを使用する。
heroku addons:add cleardb
登録が成功すると、heroku config
コマンドから作成したデータベースのURLを確認できる。
heroku config CLEARDB_DATABASE_URL: mysql://[ユーザー名]:[パスワード]@[ホスト名]/[データベース名]?reconnect=true```
herokuに作成したデータベースの設定を行う。先ほど確認したURLを元に、以下を設定する。
$ heroku config:set DB_DATABASE=[データベース名] $ heroku config:set DB_HOST=[ホスト名] $ heroku config:set DB_USERNAME=[ユーザー名] $ heroku config:set DB_PASSWORD=[パスワード]
なお、作成したデータベースにはmysql
コマンドから接続できる。
$ mysql -u [ユーザー名] -h [ホスト名]
マイグレーションの実行
clearDBはデフォルトはMySQL5.6互換であるため、AppServiceProvider
にデフォルトのインデックス用文字列長の設定を行う必要がある。
(参照:インデックス長とMySQL/MariaDB)
<?php namespace App\Providers; use Illuminate\Support\ServiceProvider; use Illuminate\Support\Facades\Schema; // 追加 class AppServiceProvider extends ServiceProvider { /** * Register any application services. * * @return void */ public function register() { // } /** * Bootstrap any application services. * * @return void */ public function boot() { Schema::defaultStringLength(191); // 追加 } }
上記変更をpushした後、migrateを実行できるようになる。
$ git add -A . $ git commit -m "デフォルトのインデックス用文字列長を191に変更" $ git push heroku master
$ heroku run php artisan migrate
Procfile 作成
herokuに、アプリを起動するために実行すべきコマンド及びアプリケーションのドキュメントルートを設定する。
設定はProcfile
を作成し、そのなかに記述する。
$ touch Procfile
Procfile
に下記を追加する(webサーバにnginxを、ドキュメントルートにpublic/
を指定している)。
web: vendor/bin/heroku-php-nginx public/
作成が完了したらpushしておく。
$ git add -A . $ git commit -m "Procfile" $ git push heroku master
APP_KEYの設定
APP_KEYが必要なのでherokuに設定しておく。
heroku config:set APP_KEY=$(php artisan key:generate --show)
ここまで設定したら、laravelの初期画面が表示されるようになる。
heroku open
参考文献
- Laravel5.7: Herokuにデプロイする
- [Heroku データベースの管理(ClearDB アドオンによる MySQL の利用)])http://www.ownway.info/Ruby/heroku/how/management/database/cleardb)
- ClearDB MySQL#Local setup
- Deploying PHP Apps on Heroku
Vuexのstate/getters/mutations/actionsについて調べた
JavaScript関連の基礎学習なう。
今回はVuexのステート・ゲッター・ミューテーション・アクションについて調べた。
ストアインスタンスの生成
Vuexを使用するには、まずストアオブジェクト
を作成し、Vueインスタンスに登録する。
ストアオブジェクト
の状態は、state
プロパティで持つことができる。
登録されたストアオブジェクトは、this.$store
を介してVueインスタンス内のすべてのコンポーネントから参照できる。
// main.js import Vue from 'vue' import Vuex from 'vuex' import App from './components/App.vue'; // Vuexの使用を宣言 Vue.use(Vuex); // ストアインスタンスの生成 const store = new Vuex.Store({ // 状態はstateプロパティで保持する state: { count: 1, } }) new Vue({ // Vueに作成したストアインスタンスを登録する。 store, render: h => h(App), }).$mount('#app')
// App.vue <template> <div> {{ count }} </div> </template> <script> export default { computed: { count () { return this.$store.state.count // 1 } } } </script>
ストアオブジェクトを使っている状態でも、本来のdataプロパティはローカルステートとして利用できる。
// App.vue <template> <div> {{ storeCount }} {{ localCount }} </div> </template> <script> export default { data: function(){ return { count: 11, } }, computed: { storeCount () { return `store count: ${this.$store.state.count}` // store count: 1 }, localCount () { return `local count: ${this.count}` // local count: 11 }, } } </script>
getters
各コンポーネントで共通のcomputedメソッドがあるとき、共通化の手段としてgetters
オプションが提供されている。
gettersに登録された関数の結果はキャッシュされ、その関数が依存しているstateの値が変更されたときにのみ再評価される。
定義したゲッターは、$store.getters
からアクセスできる(プロパティスタイルアクセス
)。
また、ゲッターの戻り値を関数にすることで、ゲッター自身に引数を渡せるようになる(メソッドスタイルアクセス
)。ただし、メソッドスタイルアクセスのゲッターについては結果はキャッシュされず、実行のたびに計算が行われる。
// main.js import Vue from 'vue' import Vuex from 'vuex' import App from './components/App.vue'; Vue.use(Vuex); const store = new Vuex.Store({ state: { count: 1, }, // getterの定義 getters: { // stateオブジェクトには、第一引数からアクセスできる storeCount (state) { return `store count: ${state.count}` }, // 関数を戻り値にすることで、getterに引数を渡せるようになる。 totalCount(state) { return function(localCount){ return `total count: ${state.count + localCount}` }; } } }) new Vue({ store, render: h => h(App), }).$mount('#app')
// App.js <template> <div> {{ storeCount }} {{ localCount }} {{ totalCount }} </div> </template> <script> export default { data: function(){ return { count: 11, } }, computed: { storeCount () { return this.$store.getters.storeCount // store count: 1 }, localCount () { return `local count: ${this.count}` // local count: 11 }, totalCount () { return this.$store.getters.totalCount(this.count) // total count: 1 }, } } </script>
mutations
ストアの状態は、ミューテーションによってのみ変更される。
ミューテーションはタイプ
とハンドラ
を持ち、ハンドラ関数は直接実行することができない。
実行するためには、store.commit
をタイプを指定して呼び出す必要がある。
また、ハンドラ関数は非同期処理を行なってはならない
。
// main.js import Vue from 'vue' import Vuex from 'vuex' import App from './components/App.vue'; Vue.use(Vuex); const store = new Vuex.Store({ state: { count: 1, }, getters: { storeCount (state) { return `store count: ${state.count}` }, totalCount(state) { return function(localCount){ return `total count: ${state.count + localCount}` }; } }, // ミューテーションの定義 mutations:{ // stateオブジェクトには、第一引数からアクセスできる // 第二引数から、commitに渡された値を受け取れる。 increment(state, payload){ state.count += payload.num; } } }) new Vue({ store, render: h => h(App), }).$mount('#app')
// App.vue <template> <div> {{ storeCount }} {{ localCount }} {{ totalCount }} <input type="button" @click="storeCountIncrement" value="increment"> </div> </template> <script> export default { data: function(){ return { count: 11, } }, computed: { storeCount () { return this.$store.getters.storeCount // store count: 1 }, localCount () { return `local count: ${this.count}` // local count: 11 }, totalCount () { return this.$store.getters.totalCount(this.count) // total count: 1 }, }, methods: { storeCountIncrement(){ // 第一引数にタイプを指定する。引数が必要な場合は第二引数に指定する。 this.$store.commit('increment', {num: 1}); // commitには以下のように、オブジェクトを渡すこともできる // this.$store.commit({ // type: 'increment', // num: 1 // }); } } } </script>
actions
アクションもミューテーションと同じくタイプ
とハンドラ
を持ち、ハンドラ関数の実行にはstore.dispatch
をタイプを指定して呼び出す。
ミューテーションとの違いは以下。
- 自身は状態を変更しない。変更するにはミューテーションをコミットする。
- 任意の非同期処理を含むことができる。
// main.js import Vue from 'vue' import Vuex from 'vuex' import App from './components/App.vue'; Vue.use(Vuex); const store = new Vuex.Store({ state: { count: 1, }, getters: { storeCount (state) { return `store count: ${state.count}` }, totalCount(state) { return function(localCount){ return `total count: ${state.count + localCount}` }; } }, mutations:{ increment(state, payload){ state.count += payload.num; } }, actions: { // 第一引数にはコンテクストオブジェクト(後述)が渡され、ここからcommitできる。 // 第二引数から、dispatchに渡された値を受け取れる。 increment(context, payload){ context.commit('increment', payload); } } }) new Vue({ store, render: h => h(App), }).$mount('#app')
<template> <div> {{ storeCount }} {{ localCount }} {{ totalCount }} <input type="button" @click="storeCountIncrement" value="increment"> </div> </template> <script> export default { data: function(){ return { count: 11, } }, computed: { storeCount () { return this.$store.getters.storeCount // store count: 1 }, localCount () { return `local count: ${this.count}` // local count: 11 }, totalCount () { return this.$store.getters.totalCount(this.count) // total count: 1 }, }, methods: { storeCountIncrement(){ // 第一引数にタイプを指定する。引数が必要な場合は第二引数に指定する。 this.$store.dispatch('increment', {num: 1}); // commitと同じく、オブジェクトを渡すこともできる // this.$store.dispatch({ // type: 'increment', // num: 1 // }); } } } </script>
本文中のコードは以下にまとめてあります。 Vuex調査
参考文献
JavaScriptのexport/importについて調べた
JavaScript再入門中。
今週はexport/import
についてメモ。
export/importとは
export 文は、モジュールから関数、オブジェクト、プリミティブな値をエクスポートするための JavaScript モジュールを作成するときに使用します。これらは別のプログラムで、 import 文で使用できます。 エクスポートされたモジュールは宣言のあるなしにかかわらず strict mode で動作します。エクスポート文は、埋め込まれたスクリプトでは使えません。
export - MDNimport 文は、他のモジュールからエクスポートされたバインディング(関数、オブジェクト、プリミティブ)をインポートするために用います。インポートされたモジュールは宣言のあるなしにかかわらずStrict モードで動作します。import 文は、type="module" を指定しない限り、埋め込まれたスクリプトでは使えません。
import - MDN
export
キーワードは、ファイルの外部からアクセス可能であるべき変数や関数にラベル付けをする。
import
キーワードは、export
されたものを読み込む。
エクスポートには、名前付きエクスポート(named exports)
とデフォルトエクスポート(default exports)
の2種類がある。
名前付きエクスポートはモジュールごとに複数持つことができるが、デフォルトエクスポートは1つしか持てない。
// 関数helloをexport export function hello(str) { console.log( `Hello, ${str}!` ); } // セミコロンはつけない(関数式にならないよう注意) // 配列fooのexport export let foo = ['bar', 'baz']; // 定数Hogeのexport export const Hoge = 'hoge'; // クラスUserのexport export class User { constructor(name) { this.name = name; } } // デフォルトエクスポート export default function(){ console.log('default exports!'); }
// 関数helloをimport import { hello } from "./sample.js"; hello('World'); //Hello World! // 配列fooをimport import { foo } from "./sample.js"; console.log(foo); //["bar", "baz"] // 定数Hogeをimport import { Hoge } from "./sample.js"; console.log(Hoge); //hoge // クラスUserをimport import { User } from "./sample.js"; console.log( (new User('tarou')).name ); //tarou // デフォルトエクスポートを変数Sampleにインポート import Sample from "./sample.js"; Sample(); // default exports!
named exports
名前付きエクスポートをimport
で読み込む際は変数名を波括弧で囲む必要があり、別名の設定や、複数同時指定などもできる。
また * as 変数名
で一括インポートできる。
// 別名の設定 import { Hoge as Fuga }from "./sample.js"; console.log( Fuga ); //hoge // 複数同時にimport import { foo, User as Tarou } from "./sample.js"; console.log(foo); //['bar', 'baz'] console.log( (new Tarou('tarou').name) ); //tarou // 一括import。すべての名前付きエクスポートをLibに読み込む import * as Lib from "./sample.js"; // プロパティ名でそれぞれのモジュールにアクセスできる Lib.hello('World'); //Hello, World! console.log( Lib.foo ); //['bar', 'baz']
default exports
デフォルトエクスポート
は、default
キーワードを付与するか、 変数名 as default
で定義する。
また、変数名を必要とせず、無名関数等でもエクスポートできる。
// 無名関数でもエクスポート可(デフォルトエクスポートは1モジュールにつき1つであるため) export default function (){ /*...*/ } //同一モジュールに複数 default が定義されているとエラー export default class{ constructor() { ... } } //SyntaxError: Duplicate export of 'default'
// デフォルトエクスポート let Hoge = "default exports"; // 以下は export default Hoge と等価 export { Hoge as default }; // 名前付きエクスポート let Foo = "named exports"; export { Foo };
import
で読み込む際に波括弧を指定しなかった場合、デフォルトエクスポートが読み込まれる。
また、default
キーワードを使用することで別名をつけたり名前付きエクスポートと同時にimportしたりできる。
// 波括弧をつけないとデフォルトエクスポートがインポートされる import Hoge from "./sample_2.js"; console.log( Hoge ); //default exports // defaultキーワードの別名としてインポート import { default as Fuga }from "./sample_2.js"; console.log( Fuga ); //default exports // 名前付きと同時にimport import { default as Piyo, Foo } from "./sample_2.js"; console.log(Piyo); //default exports console.log( Foo ); //named exports // 一括importした場合、defaultプロパティにデフォルトエクスポートが入っている import * as Lib from "./sample_2.js"; console.log( Lib.default); //default exports console.log( Lib.Foo ); //named exports
import() 関数
import
キーワードにはいくつかの制約がある。
- ブロックの中に置くことはできない
- モジュールパスを変数などで動的に生成することはできない
// ブロックのなかに置くことはできない if(true){ import { hello } from "./sample.js"; // Uncaught SyntaxError: Unexpected token { } // パスにプリミティブな文字列以外を使用することはできない。 let path = "./sample.js"; import { hello } from path; // Uncaught SyntaxError: Unexpected identifier
動的にimportする必要がある場合、import()関数
を使用する。
import()関数はPromise
オブジェクトを返却するただの関数であるため、import
キーワードのような制約を受けずに使用できる。
let path = "./sample.js"; import(path) .then((obj) => { // 各モジュールには、引数objのプロパティからアクセスできる。 obj.hello('World'); obj.default(); });
本文中のコードは以下にまとめてあります。 export/import調査
参考文献
JavaScriptのasync/waitについて調べた
JavaScript再入門中。
今回はasync/wait
についてメモ。
async/waitとは
async function 宣言は、 AsyncFunction オブジェクトを返す 非同期関数 を定義します。非同期関数は非同期でイベントループを介して実行され、暗黙的にPromiseを返します。なおコードのシンタックス及び構造は通常の同期関数と非常に似たものになります。
async function - MDNawait 演算子は、async function によって Promise が返されるのを待機するために使用します。
wait - MDN
async/await
を利用することで、Promise
処理をより簡潔に書くことができる。
async function
async function
は、関数定義の前にasync
キーワードをつけて宣言する。
// async functionの定義 var asyncFunc = async function () { return 1; } // async functionは実行するとPromiseオブジェクトを返却する asyncFunc().then( console.log ); // 1
async関数は、return
で終了した場合はresolve
された、throw
で終了した場合はreject
されたPromise
オブジェクトを返却する。
また、自前で作成したPromise
オブジェクトを返却することもできる。
以上から、async関数の戻り値はPromiseオブジェクトであることが保証される。
var asyncFunc1 = async () =>{ return 1 }; asyncFunc1() .then( v => console.log(`then: ${v}`) ) .catch( v => console.log(`catch: ${v}`) ); // then: 1 var asyncFunc2 = async () =>{ throw new Error('error')}; asyncFunc2() .then( v => console.log(`then: ${v}`) ) .catch( v => console.log(`catch: ${v}`) ); // catch: Error: error // returnを明示しなかった場合はresolveになる var asyncFunc3 = async () =>{ }; asyncFunc3() .then( v => console.log(`then: ${v}`) ) .catch( v => console.log(`catch: ${v}`) ); // then: undefined // 明示的にPromiseオブジェクトを返却することもできる var asyncFunc4 = async () => Promise.resolve(4); asyncFunc4() .then( v => console.log(`then: ${v}`) ) .catch( v => console.log(`catch: ${v}`) ); // then: 4 var asyncFunc5 = async () => Promise.reject(5); asyncFunc5() .then( v => console.log(`then: ${v}`) ) .catch( v => console.log(`catch: ${v}`) ); // catch: 5
await
await
キーワードは、async関数の実行を一時停止させ、Promise
の解決または拒否を待機する。
var asyncFunc = async () => { let promise = new Promise((resolve, reject) => { setTimeout(() => resolve("success!"), 1000) }); let result = await promise; // promise が解決するまで待機 console.log(result); } asyncFunc(); // success!
await
キーワードは、async関数以外の箇所では宣言できない。
グローバルや、通常の関数内部で実行するとシンタックスエラーが発生する。
// 通常の関数内部では使用できない var func = function(){ let promise = await new Promise((resolve, reject) => {}); await promise; // Error: await is only valid in async function }
await
に渡されたPromise
がrejectされた場合、通常の例外のように値がスローされる。
スローされた値は、例外のようにtry..catch
でキャッチできる。
また、Promise.catch
メソッドで処理することもできる。
// try..catch構文を使って処理 var asyncFunc1 = async () => { try { var foo = await Promise.reject(new Error('failed..')); // Errorオブジェクトがスローされる } catch (e) { console.log(e); } } asyncFunc1(); // Error: failed.. // catchメソッドを使っても処理できる var asyncFunc2 = async () => { var z = await Promise.reject(new Error('failed..')).catch(console.log); } asyncFunc2(); // Error: failed..
記事中のコードは以下にまとめてあります。
async/wait調査 · GitHub
参考文献
JavaScriptのPromiseについて調べた
JavaScript再入門中。
async/await
を覚えるにあたってPromise
をおさらいしないといけなさそうだったのでメモ。
Promiseとは
Promiseは非同期処理の最終的な完了もしくは失敗を表すオブジェクトです。 Promiseを使う - MDN
// 構文 new Promise( function(resolve, reject) { ... } );
Promiseの状態
生成されたPromise
オブジェクトは、以下の状態を持つ。
- pending: 初期状態。成功も失敗もしていない。
- fulfilled: 処理が成功して完了した。
- rejected: 処理が失敗した。
Promise
コンストラクタに与えられた関数(executor
関数)には、2つの引数resolve
関数とreject
関数が渡される。
executor
関数は内部でなんらかの非同期処理を実行し、
成功した場合にはresolve
関数を呼び出すことでfulfilled
に、
失敗した場合にはreject
関数を呼び出すことでrejected
にPromise
オブジェクトの状態を変更できる。
また、executor
関数で例外がスローされた場合、そのPromise
オブジェクトの状態はrejected
となる。
var promise1 = new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); xhr.open("GET", 'http://example.com'); xhr.onload = () => resolve(xhr.status); // resolveは成功を表す xhr.onerror = () => reject(xhr.status); // rejectは失敗を表す xhr.send(); }); var promise2 = new Promise((resolve, reject) => { throw new Error('error!'); // 例外がスローされた場合はrejected });
then / catch / finally
生成したPromise
オブジェクトは、インスタンスメソッドthen
を使って成功時/失敗時の処理を登録することができる。
Promise
オブジェクトが成功(fulfilled
)時には第一引数の関数が、失敗(rejected
)時には第二引数の関数が実行される。
また、then
の他に、失敗時の処理のみを登録できるcatch
メソッド、Promise
オブジェクトの成功/失敗に関わらず必ず実行されるfinally
メソッドがある。
promise.then( function(value){ /* 成功時の処理 引数valueには、reslve()に渡された値が入っている */ }, function(reason){ /* 失敗時時の処理 引数reasonには、reject()またはError()に渡された値が入っている */ } ); promise.catch( function(reason){ /* 失敗時時の処理 */} ); //catchを使った書き方は、以下の書き方と等価 promise.then( undefind, function(reason){ /* 失敗時時の処理 */} ); promise.finally(function(){ /* 成功/失敗共通の処理 finallyメソッドは引数をとらない。 */ });
Promiseチェーン
実行されたthen
/catch
/finally
メソッドは、新しくPromise
オブジェクトを返す。
そのため、メソッドチェーンのように書くことができる。
var promise = new Promise(function(resolve, reject) { setTimeout(function() { resolve(0); }, 300); }); promise .then( value =>{ console.log(value); // コールバック関数内でreturnすることで、値を後続の処理に引き渡せる。 return ++value; }) .then( value =>{ console.log(value); return ++value; }) .then( value =>{ console.log(value); return ++value; }) ; // 出力は以下のようになる // 0 // 1 // 2
なお、メソッドチェーンの途中で例外が発生した場合、その処理は中断され、後続の最も近いcatch
メソッドもしくはthen
のエラー処理関数が実行される。
var promise = new Promise(function(resolve, reject) { setTimeout(function() { resolve(0); }, 300); }); // catchによる例外処理 promise .then( value =>{ console.log(value); return ++value; }) .then( value =>{ // 例外をスロー throw new Error('Error!'); }) .then( value =>{ // 前の処理で例外がスローされているため、この処理は実行されない console.log(value); return ++value; }) .catch(error => { console.log(error); }) ; // 出力は以下のようになる // 0 // Error: Error!
var promise = new Promise(function(resolve, reject) { setTimeout(function() { resolve(0); }, 300); }); // thenのエラー処理関数による例外処理 promise .then( value =>{ console.log(value); return ++value; }) .then( value =>{ // 例外をスロー throw new Error('Error!'); }, error =>{ // 同じthenメソッドに登録されているエラー処理関数は実行されない。 console.log(`then1: ${error}`); }) .then( value =>{ // 前の処理で例外がスローされているため、この処理は実行されない console.log(value); return ++value; }, error =>{ console.log(`then2: ${error}`); }) .catch(error => { // 前処理のthenで例外が処理されているため、この処理は実行されない。 console.log(`catch: ${error}`); }) ; // 出力は以下のようになる // 0 // then: Error: Error!
Promise.resolve と Promise.reject
Promise
オブジェクトの生成にはnew Promise()
の他にもシンタックスシュガーとしてPromise.resolve
およびPromise.reject
という静的メソッドが用意されている。
Promise.resolve
は成功状態の、Promise.reject
は失敗状態のPromise
オブジェクトを返却する。
Promise.resolve('success'); // 上記は以下と等価 new Promise(function(resolve){ resolve('success'); }); Promise.reject(new Error('error!')); // 上記は以下と等価 new Promise(function(resolve,reject){ reject(new Error('error!')); });
Promise.all と Promise.race
複数のPromise
オブジェクトをまとめて処理する方法として、Promise.all
と Promise.race
がある。
Promise.all
は引数にPromise
オブジェクトの配列を取り、すべてのオブジェクトが成功状態のとき後続のthen
の成功時処理を実行する。ひとつでも失敗するとthen
は実行されず、catch
またはthen
の失敗処理が実行される。
Promise.all([ Promise.resolve(1), Promise.resolve(2), Promise.resolve(3), ]) .then(values =>{ console.log(values[0]); console.log(values[1]); console.log(values[2]); }); // 出力は以下 // 1 // 2 // 3 Promise.all([ Promise.resolve(1), Promise.reject(new Error('error')), Promise.resolve(3), ]) .then(values =>{ console.log(values[0]); console.log(values[1]); console.log(values[2]); }) .catch(error =>{ console.log(error); }); // 出力は以下 // Error: error
Promise.race
もまた引数にPromise
オブジェクトの配列を取り、どれか一つでも成功または失敗の状態になったら後続の処理を実行する。
var promise = Promise.race([ new Promise(function (resolve, reject) { const xhr = new XMLHttpRequest(); xhr.open("GET", 'http://example.com'); xhr.onload = () => resolve(xhr.status); xhr.onerror = () => reject(xhr.status); xhr.send(); }), new Promise(function (resolve, reject) { setTimeout(() => reject(new Error('request timeout')), 5000) }) ]); promise .then(value => console.log(value)) .catch(error => console.log(error));
なお、後続の処理に引き渡されるのは最初に解決したPromise
オブジェクトの結果のみだが、他の処理が中断されるわけではないので注意が必要。
var promise = Promise.race([ new Promise(function (resolve, reject) { console.log('promise 1'); setTimeout(() => reject(new Error('request timeout')), 5000) }), new Promise(function (resolve, reject) { console.log('promise 2'); setTimeout(() => reject(new Error('request timeout')), 1000) }) ]); promise .then(value => console.log(value)) .catch(error => console.log(error)); // 出力は以下 // promise 1 // promise 2 // Error: request timeout
記事中のソースは以下にまとめてあります。
参考文献
JavaScriptのアロー関数について調べた
数年振りにJavaScriptを触ることになりそうなので、改めて今時の仕様をきちんと理解したいと思いいろいろ調べている。 今回はアロー関数について調べたのでメモ。
アロー関数とは
アロー関数式は、より短く記述できる、通常の function 式の代替構文です。また、this, arguments, super, new.target を束縛しません。アロー関数式は、メソッドでない関数に最適で、コンストラクタとして使うことはできません。
アロー関数 - MDN
// 通常のfunction式による関数定義 var hoge = function(){ console.log('hoge'); }; // アロー関数式を使った定義 var hoge = () => { console.log('hoge'); };
短縮構文
アロー関数では、引数の()やブロックの{}を省略できる場合がある。
// 引数がひとつの場合、()を省略できる。 var hoge = arg => { return arg }; console.log(hoge(1)); // 1 // 引数が複数の場合、()は省略できない。 var hoge = arg1, arg2 => { return arg1 + arg2 }; console.log(hoge(1)); // SyntaxError: Unexpected token => // 本体が単一の式の場合、{}およびreturnを省略できる()。 var hoge = arg => arg + 1; console.log(hoge(1)); // 2 // 戻り値がオブジェクトリテラルの場合、()を付与する必要がある。 var hoge = arg => ({foo: arg}); console.log(hoge(1)); // { foo: 1} // 本体が複数の式の場合、{}は省略できない。 var hoge = arg => arg + 1; arg + 2; console.log(hoge(1)); // ReferenceError: arg is not defined
this
通常の関数は、関数ごとに自身のthisを定義するが、 アロー関数式のthisは、自身のthisを持たず、レキシカルスコープのthisを使用する。 つまり変数探索ルールと同じような挙動をする(現在のスコープ内にthisがない場合、外側のスコープを探索し、最終的にグローバルスコープに到る)。
// function式 const tarou = { family_name: '山田', first_name: '太郎', fullname: function(){ console.log(`${this.family_name} ${this.first_name}`); } }; tarou.fullname(); //山田 太郎 // アロー関数式 const hanako = { family_name: '山田', first_name: '花子', fullname: () =>{ console.log(`${this.family_name} ${this.first_name}`); } }; // hanako.fullname()内のthisは外側のthis(=グローバルオブジェクト)を // 参照しているため、undefinedが返る。 console.log(hanako.fullname()); //undefined undefined
コールバック関数を利用する場合、従来のfunction式ではthis
がグローバルオブジェクトを参照するため、呼び出し元のthisを参照したい場合var self = this
のように変数に一時代入しなければならなかったが、アロー関数式は実行時のスコープ内のthisを参照するためその必要がなくなった。
const hoge = { count: 0, // コールバック関数に通常の関数を渡す func: function(){ setTimeout(function(){ // このthisはグローバルオブジェクトを参照している this.count++; console.log(this.count); }, 1000) }, // コールバック関数にアロー関数を渡す arrowfunc: function(){ setTimeout(()=>{ // このthisは外側のthis(=hoge)を参照している this.count++; console.log(this.count); }, 1000) } }; hoge.func(); // NaN hoge.arrowfunc(); // 1
参考文献
PHPでMarkdownをHTMLに変換するparsedownを試した
Markdownをパースするライブラリを調べたところparsedownが良い感じだったのでメモ。 文中のPHPは7.2を使用。
準備
composerでインストール。
$ composer require erusev/parsedown
実際に変換してみる
<?php require_once "./vendor/autoload.php"; $Parsedown = new Parsedown(); // safeModeをtrueにしておくとエスケープしてくれる。 $Parsedown->setSafeMode(true); $md = <<<EOF # 見出し1 parsedownのサンプルです。 parsedownのサンプルです。 parsedownのサンプルです。 * 箇条書き * 箇条書き *太文字* |題名|題名|題名| |:---|:---:|---:| |左寄せ|中央寄せ|右寄せ| <script> alert('hoge'); </script> EOF; // 出力 echo $Parsedown->text($md);
上記の例だと、以下のようなhtmlが出力される。
<h1>見出し1</h1> <p>parsedownのサンプルです。<br /> parsedownのサンプルです。<br /> parsedownのサンプルです。</p> <ul> <li>箇条書き</li> <li>箇条書き</li> </ul> <p><em>太文字</em></p> <table> <thead> <tr> <th style="text-align: left;">題名</th> <th style="text-align: center;">題名</th> <th style="text-align: right;">題名</th> </tr> </thead> <tbody> <tr> <td style="text-align: left;">左寄せ</td> <td style="text-align: center;">中央寄せ</td> <td style="text-align: right;">右寄せ</td> </tr> </tbody> </table> <p><script> alert('hoge'); </script></p>
ブラウザでも確認。
簡単にMarkdown->htmlの変換に対応することができた。
参考文献
本家(デモ有)
Better Markdown Parser in PHP
Github
GitHub - erusev/parsedown: Better Markdown Parser in PHP