この画面は、簡易表示です
Promiseオブジェクトの作り方には大きく2つある。
new Promise( ) は、新たにオブジェクトを作るけど、Promise.resolve( ) の方はstaticなPromiseオブジェクトを使いまわししているだろう。
class Promise {
constractor () {
・・・
}
static staticResolve = new Promise().resolve(undefined);
resolve() {
if (typeof(this) === "undefined") {
return Promise.staticResolve;
・・・
}
}
と考えると、Promise.resolve( ) はpromise状態が履行 (fulfilled)に確定しているところから始まるから
Promise.resolve().then( (a)=>{
/* なんたら */
}).then( (b)=>{
/* かんたら */
}).then( (c)=>{
/* それから */
}).then( (d)=>{
/* こうして */
}).then( (e)=>{
/* こうなった */
}).catch((e)=>{
/* どうして、そうなった? */
});
と書いても、「なんたら」部の最後まで進めば、直ぐに「かんたら」に突入し、ストレートに「こうなった」まで進んで一気に進むので、完全な同期処理である。
ただ、ドコが壊れているのかイミフなコードを切り分けた状況のエビデンスを作る時には非常に便利である。
実際には<% try { %><% throw(“ココか?”); %> <%} catch(e) {consol.log(e); } %>式に
プローブ針を垂らすけど、エビデンスとしては判りにくいからね。
強いて言えば、「なんたら」「かんたら」「それから」「こうして」「こうなった」の全てにawaitで強制的にワーカースレッドを停止させる箇所が無いと意味が無いのだ。
そんな訳で、非同期で処理したいものを1つづつ、「非同期化」するしかないが、そう難しくはない。
class hoge {
// 非同期型 同期型と同じ数だけ作成する
asyncFunc(params) {
return new Promise((resolve, reject) => {
try {
let result = syncFunc(params);
resolve(result);
} catch(e) {
reject(e);
}
});
}
// 同期型
syncFunc(params) {
return foo;
}
・・・
}
と非同期呼び出し口を用意するだけ、
しかし、async接頭子のメソッドがいっぱい増えるのが嫌なら
class hoge {
// 非同期型 1つだけ作って使いまわし。※毎回 new Promise しなくていいのが嬉しい。
asyncCall(syncFunc,params) {
return new Promise((resolve, reject) => {
try {
let result = syncFunc(params);
resolve(result);
} catch(e) {
reject(e);
}
});
}
// 同期型
syncFunc1(params) {
return foo;
}
・・・
// 同期型
syncFuncN(params) {
return foo;
}
}
と、非同期化メソッドを用意しておくといいだろう。
引き渡すパラメータはthenチェインする前に確定してしまうから、後々トラブりそうなのが難点で、
しかも、下のコードを見て判るように、各処理に引き渡すパラメータは事前に用意することになるけど
paramを変数宣言しておいて使用すれば、各処理が動き出す前に調整は可能だし、何よりデバッグが楽だ。
ちゃんとデバッグが済んでから、試作型に実処理を埋め込めばいいだろう。
とりあえず、
// 非同期処理を準備する
let param1;
let param2;
let param3;
let param4;
let param5;
let promFunc1 = asyncFunc1(param1);
let promFunc2 = asyncFunc2(param2);
let promFunc3 = asyncFunc3(param3);
let promFunc4 = asyncFunc4(param4);
let promFunc5 = asyncFunc5(param5);
//
promFunc1.then(()=>{ /* なんたら param2を最新化 */
promFunc2.then(()=>{ /* かんたら param3を最新化 */
promFunc3.then(()=>{ /* それから param4を最新化 */
promFunc4().then(()=>{ /* こうして param5を最新化 */
promFunc5().then(()=>{ /* こうなった */
});});});});}).catch((e)=>{ /* どうして、そうなった? */
});
とコードすることで完全に非同期になると思う。
でも、thenチェインって云われているものは普通コレではない。
//同期っぽい書いてある非同期処理 ※パラメータは暗黙の了解で繋がっている
promFunc1(a) /* なんたら 暗黙のparam2を最新化 */
.then(promFunc2) /* かんたら 暗黙のparam3を最新化 */
.then(promFunc3) /* それから 暗黙のparam4を最新化 */
.then(promFunc4) /* こうして 暗黙のparam5を最新化 */
.then(promFunc5) /* こうなった */
.catch((e)=>{
/* どうして、そうなった? */
});
あるいは
//同期っぽく書いてある非同期処理
promFunc1(a).then( (b)=>{ /* なんたら */
let b = await promFunc2(b); /* かんたら */
return b;
}).then( (b)=>{
let c = await promFunc3(b); /* それから */
return c;
}).then( (c)=>{
let d = await promFunc4(c); /* こうして */
return d;
}).then( (d)=>{
let e = await promFunc5(d); /* こうなった */
return e;
}).catch((e)=>{
/* どうして、そうなった? */
});
のハズだ。この書き方の最大の問題点はawaitが本当に動作してくれるかどうかだ。
awaitはドコかのPromiseオブジェクトが「履行 (fulfilled): 処理が成功して完了」になるのを待つせいで、
ワーカースレッド内で同時に使用できるのはたったの1回だけ。
ネストできないから、上のコードを参照する処理や各async内でawaitしてるとawaitした途端にエラってしまう。
ので、
つまり、awaitのaは、「とあるドコかの何か」ではなく「お一人様」の意味なのだ。
※試験には出ないけど重要です。
流石、文化が違うよね。ボクらはネストしてるthenチェーンか綺麗になおしたらデバッグ困難なコードに立ち向かう事になる。
だから、「ボタンをクリック」した直後に一回くらいに限定した方がいい。
サーバーと通信する際に使うと、画面隅に通知機能を搭載した途端にケンカ沙汰になってしまう。
というのが、ググった結果から得られた感想。
本当にこうなっているのかコードしてみると予想が結構ハズれているような気がする。
何せ、数年経てば、仕様書上は全く変わらないのに、全く違う動作になっている世界だけに。
実際、new Promiseのコールバックは今では即実行されてしまうので、
Promiseのthenの設定を全て終えメインスレッドが完了してから動いてくれると思ったら大間違い。
Promise.thenのコードを見つけると即実行するので、全く非同期ではない。
正確には、new Promiseのコールバックの中だけ、resolveかrejectを呼ぶまで非同期になってるダケで
Promise.resolveは、非同期に見える振りをしているだけ。
非同期にしたいなら
//同期っぽい書いてある非同期処理 ※パラメータは暗黙の了解で繋がっている
promFunc1(a) /* なんたら 暗黙のparam2を最新化 */
.then(promFunc2) /* かんたら 暗黙のparam3を最新化 */
.then(promFunc3) /* それから 暗黙のparam4を最新化 */
.then(promFunc4) /* こうして 暗黙のparam5を最新化 */
.then(promFunc5) /* こうなった */
.catch((e)=>{
/* どうして、そうなった? */
});
の書き方以外は意味が無い。
正解を引き当てたると後々面倒なことになるのはいつもの事。(大笑
「非同期な処理」≒「後でやって欲しい処理」を書くには、
各promFuncNの中でsetTimeoutしてresolveする以外に方法は無い。
ps.2021/12/13
サンプル追加。
thenチェインする処理を関数化すると、returnした値が引き継がれないのは痛いね。
thenチェインでreturnした値は、thisにぶら下げて引き渡しているんだろう。
1つの関数の中で閉じ籠って then チェイン して
うまく動いてるけど、ゴチャゴチャしているので
リファクタリングして美しいthenチェインにすると・・・
壊れます。(痛すぎ
イミフなスタックオーバーフローに悩まされた。
static dataDumpNest = [];
static DATADUMPNEXT_MAX = 100;
static dataDump(objectName, obj, pOptions) {
try {
・・・
Helper.dataDumpNest.push(obj);
rc = true;
・・・
return true;
・・・
// 再帰呼び出し時はtrueを返す
if (Helper.dataDumpNest.length > 1) {
return true;
}
// それ以外は result を結合し返す
return Helper.result.join("\n");
} catch (e) {
throw e;
} finally {
Helper.dataDumpNest.pop();
}
}
finallyで無条件でpopしているので、
重複チェックで弾かれてpushしてない時に
無駄にjoinしてしまうのが気になり
執拗にF10でトレースしていると・・・
finallyブロックの終わりで呼び出し元のメソッドに戻るべきところが、
という摩訶不可解な現象にぶち当たった。
どうやら、return文無しで、finallyブロックの末尾にぶち当たると、ヤバイ事が起きている。
1万個の配列を何度もjoinさせ、しかも結果は捨てているのでブチ切れさせてしまったかもね。(大笑
普通、ドコかのreturn文の後に処理するハズのfinally句なのに。
延々と待たされた挙句にスタックオーバーフローになるソースを
finally句でtryの外の変数rcをreturnする様にしてみた。
static dataDumpNest = [];
static DATADUMPNEXT_MAX = 100;
static dataDump(objectName, obj, pOptions) {
let rc = false;
try {
・・・
Helper.dataDumpNest.push(obj);
rc = true;
・・・
rc = true;
return rc;
・・・
// 再帰呼び出し時はtrueを返す
if (Helper.dataDumpNest.length > 1) {
rc = true;
return rc;
}
// それ以外は result を結合し返す
rc = Helper.result.join("\n");
return rc;
} catch (e) {
throw e;
} finally {
if (rc != false) {
Helper.dataDumpNest.pop();
}
return rc;
}
}
と変えたら、即終わったった。
setTimeoutでワーカースレッドっぽい状態なんで、
finallyが、Promiseと勘違いしてスタックを遡ってみると配列がいっぱい積んであるので
Array[ Array[1], Array[2] ] あたりが、
new Promise((resolve, reject) {
・・・
}
に、そっくりだったのかもしれない。
しかし、違うと気が付いて、throw new Error(“Don’t mind.”);
して、普通のfinally になるつもりが、ソコのfinallyでまた躓いて、無限ループしてそう。
でも、そのうち治るでしょ?
やっと、本題の文法解析に戻れそう。
ん?三国英雄たちの夜明けがフリーズしやすいのは
もしやコレなのか?
JavaScriptは型が無い。とは云え、全く無視するのは辛いので。マメにJSDocなコメントを入れている。
VisualStudioCodeでは関数や変数の名前をクリックして2回(Ctl+Alt+D)すると、テンプレが書き込まれるので簡単だ。
しかし、コード中に型チェックすると typeof を羅列するより switch case で書きたい時もある。
そんな時はObject.prototype.toString()が、
[Object ${ObjectTypeName}]の様な文字列を返してくれるので、 switch case 風に処理できる。
/**
* オブジェクトの型名を調べる
*
* @param {Any} obj オブジェクト
* @return {string} 型名
*/
const getTypeName= (obj) => {
if (typeof (obj) === "undefined") {
return "undefined";
}
var toString = Object.prototype.toString;
// [object typeName] のハズなので、typeName部分を抽出してみる
let typeName = toString.call(obj).match(/^\[\w+\s+(\w+)\]$/);
return typeName[1];
}
各オブジェクトのtoStringメソッドは、一番OverWriteしまくっているメソッドなので、
ココでは確実な動作を狙って、Object.prototype. toString をCall呼び出しで参照してみた。
でも名前がgetTypeNameなのがイマイチなんでObjectTypeNameにリファクタリングしたら、
他のファイルでUndefinedになってた。
JavScriptだから仕方が無いけど。
置換機能で書き換えようとしたら・・・
(サッパリ
検索すらしない。
ググっても、それっぽい記事は無い。
試しに「検索エディタ」を開いてみた。
するとちゃんと検索しだした。
MSCodeを再起動しても、結果は良好。
つまり、裏側でこっそり設定ファイルが出来た様だ。
※この手の自動設定の不具合は、インストーラのテスト同様に見つけにくいものだ。
昔の映画で「xx製は叩けば治る!」というセリフがあったけど、MSCodeも同じらしい。
初回ダメなら「新規xx」とかをぶったたけば、初期設定ができるかも。
しかし、今後はどうなるか予断を許さない。(と思う。
Java パーサコンビネータ 超入門というのを見つけた。
使い方を説明してくれるのでとても助かるけど、
JavaScriptでパーサコンビネータのコンセプトを理解する(「正規表現だけに頼ってはいけない」の続き)
の方がパーサ・コンビネータの仕組みを理解でき、且つ、使わなくなったけどJavascriptの便利な手法を思い出させてくれた。
function 関数(...) {
return [1,2,3,4, ...];
}
と配列等で容易に複数の値を変えすことができるし、そんな関数を
let [a, b, c, d, ...] = 関数(...)
と呼び出すと、すんなり配列中の a, b, c, d, … に値を入れてくれるトコが地味に嬉しい。
いつのまにかいっぱい返す事になる未来が待っていそうな気がするから new classを返すことが多いけど。
また
function token(str) {
var len = str.length;
return function(target, position) {
if (target.substr(position, len) === str) {
return [true, str, position + len];
} else {
return [false, null, position];
}
};
}
の様にreturn function {…} することで、関数ジェネレータを簡易に記述できるのは便利、
但し、呼び出す側が、
token('foobar')('foobar', 0); // => [true, 'foobar', 6]を返す
な感じになってしまうとワークスペース内のファイルの関数を全部チェックする開発環境ではなく、
sakuraエディタで チョコ チョコ叩いている場合は、【見た目文法エラー!】なのが難点。
やはり、
const func = token('foobar');
func('foobar', 0); // => [true, 'foobar', 6]を返す
と書いて関数が戻ってくる雰囲気を醸し出した方が無難な気がする。
many(token('hoge'))('hogehoge', 0); // => [true, ['hoge', 'hoge'], 8] を返す
も、token(‘hoge’)部分がmanyな場合は、あーもーな感じになりそうなんで
let parsers = [];
parsers.push(token('hoge1'));
...
parsers.push(token('hogeN'));
let text = 'hoge1hoge2...hogeN';
let myParser =seq(parsers);
myParser(text, 0); // => [true, ['hoge1', ['hoge2', ...['hogeN']...]]], 8] を返す
と1行がいっぱい膨れ上がってしまいそう。
てか、実際に’hoge1hoge2’と直書きすることは滅多に無いから
もっと、もっと膨れ上がる。
先の記事のソースは「あくまで説明しやすく表記」したものなのは明白だけど、
テストコードってこんな風に書いてたりするよね?。(笑
ps.
先の「理解する」のページのコードをまとめてみた。
簡易のテストケースもパーサーの種類ごとに1つダケ書いてみたら、結構長くなってしまった。
作りかけだからFalseしまくってるのはいいとして
Errorオブジェクトから発生原因を判りやすくしてほしいなぁ。
字句解析とか構文解析は、「,」1つの修正だけでも
見えないところがトンデモない結果になり果ててしまいがちなので、テストはガチでやった方がいいからね。
※ん-。まだまだな感じがする。
そもそも、各パーサはサブクラス化した方が良いんじゃにのかな?
更にパーサ コンテナ クラスで括った方が・・・。
アカン。どんどん膨れ上がる一方だ。
ps.2021/12/3
BNFっぽい文法”Call = [ Call ] procedureName [ (argumentList) ] ”と書かれたテキストを
解析するコードを作成中。やっと単純な文法をクリアしたものの、2つ目の文法に進まない。
デバッグでハマりすぎ長いログをスクロールするのが大変。
コードの見直しが多すぎて、メソッド単体テストが壊滅状態。
修正してみたら、半壊状態。←いまこのへん。
全文法(287行中7行)をクリアするのはまだまだ先の話。
本当に終わりが見えないなぁ(大笑
// syntax = [ rule ] ;
setSyntax("syntax", many(refSyntax("defsymbol")));
// defsymbol: { "\n" } IDENTIFIER "=" rule
//###setSyntax("defsymbol", seq(optEol, identifire, asign, refSyntax("rule")));
setSyntax("defsymbol", seq(optEolWs, identifire, asign, refSyntax("rule")));
// rule: list { "|" list }
setSyntax("rule", seq(refSyntax("list"), repeat(seq(vl, refSyntax("list")))));
// list: { seq | block | repeat | option } "\n" { notes }
// TODO: repeat(...)の中にoption系が挟まると無限ループするので、何か対策案を考える
setSyntax("list", seq(repeat(choice(refSyntax("seqList"), refSyntax("block"), refSyntax("repeat"), refSyntax("option"))), eol, opt(refSyntax("notes"))));
// seqList = seq { seqList }
setSyntax("seqList", seq(refSyntax("seq"), repeat(seq(/*ws,*/ refSyntax("seqList")))));
// seq = (regExp | IDENTIFIER | STRING | INTEGER | , )
setSyntax("seq", choice(reExFmt, identifire, string, number, comma));
// block: "(" rule ")"
setSyntax("block", seq(lpr, refSyntax("rule"), rpr));
// repeat: "{" rule "}"
setSyntax("repeat", seq(lbr, refSyntax("rule"), rbr));
// option: "[" rule "]"
setSyntax("option", seq(lbk, refSyntax("rule"), rbk));
// notes: "#Notes" "Syntax" eol rule "}}" "#Notes" "End" eol
setSyntax("notes", seq(notes, ws, syntax, eol, refSyntax("syntax"), notes, ws, end, eol));
見直すのはコレだけなのに。
クリアしたら、
先はまだまだ長い。
ps.2021/12/6
サブクラス化したもののソースは1000行を越えた。何か間違えている様だ。
サブクラス化 の過程で
18行1列まで読めた。 行末の _ 結合演算子がムズイ。
ps.2021/12/7
事前にザックリと空白、改行、その他でトークン化したついでにコメント文を空白化。後は _ かな。
全文からワード単位で字句解析するようになってブラウザの動作は軽くなった気がする。
ワード単位で取り出す時に、文末が” _”なら、次の改行を無視すればいいのかな?
ps.2021/12/8
手直しをしたら余計悪化。
0回ループOKなRepeatがネストしたら無限ループしてしまうので、Sequenceで評価がTrueでもpositionが進まない時は、評価をFalseにしたけど、まだループ。
仕方が無いので、catch exceptionで強制停止。300万行とか凄い行数になってたので、1000行までに制限。
やっと途中経過が可視化できた。ログのネストを深く掘り下げると
ParserInfo (ParserInfo)で、字句解析まで見れるけど。文法解析まで見れない。
ps.2021/12/9
手直し。 ダンプの仕方を見直し。
もう、本題を忘れてダンプに力が入る。
ps.2021/12/11
手直し。 ダンプの仕方を見直し。 TreeView.markingをPromiseってみた。設定できてるけど、処理が終わったと返ってこない。
ps.2021/12/13
関数のダンプが修正漏れでエラっていたを訂正。
1.Windows Update
「準備ができたしたーし」
かかか、噛みました的な。
2.手動アップグレード リンク先のページから以下を選択
(1)Windows 11 インストール アシスタント
(2)Windows 11 のインストール メディアを作成する
(3)Windows 11 ディスク イメージ (ISO) をダウンロードする
(4)その他のダウンロード 企業向け
3.お試し方法
Hyper-Vで、手軽にお試しできる。
とりあえず
やっぱりPCで動かしたいなら
しかし、急にWindowsUpdateがいくつか見つかり再起動すると、
色々できる(ハズ
EXEからアイコンを取り出す方法をさがしていたら、PowerShellでもできるらしい。
※Ver6からはSystem.Drawing.Iconが無い様だ。Windows10はVer5だから動くけど。
しかし、文法が ソースっぽさとコマンドラインが混ざってる(メンドクサイ
例えば、PowerShellの窓を開き
元ネタ:https://suzuzusu.hatenablog.com/entry/2016/10/14/043726
###########################################################
# アイコンを抽出して保存
###########################################################
function ExtractionIcon(
$Path, # 抽出する .exe のフルパス
$OutFile # 出力する .ico のフルパス
){
# アセンブリロード
Add-Type -AssemblyName System.Drawing
Add-Type -AssemblyName System.IO
$str1 = "ExtractionIcon :Path=`"$Path`", :OutFile=`"$OutFile`""
$str1
# アイコンデータ抽出
$IconData = [System.Drawing.Icon]::ExtractAssociatedIcon( $Path )
# 出力用ファイルストリーム
$FS = New-Object System.IO.FileStream( $OutFile, [System.IO.FileMode]::Create )
# 保存
$IconData.Save( $fs )
$fs.Close()
# オブジェクトの始末
$fs.Dispose()
$IconData.Dispose()
}
と貼り付けると、
窓が開いている間だけ使える。(便利
しかし、この関数を呼び出す時はコマンドプロンプトっぽく
> ExtractionIcon "C:\Program Files (x86)\Sarad\DiCE\dice.exe" "a:\aaa\aaa.ico"
と打つ。
関数の中では、関数っぽく呼び出すのだから・・・
コマンドプロンプトから1コマンドづつ打って動作を確かめた後、
関数化する際には
のが面倒だな。
後、Windowsでパパっと使う都合上、デスクトップに貼る場合、
Join-Path $env:UserProfile\Desktop "sample.ico"
で済ましたいがOneDriveをインストしてしまうと、アンスコしてもパスが変わってしまうから
Join-Path [System.Environment]::GetFolderPath("Desktop") "sample.ico"
と書かないと動かないPCも出てくる。
更に、コマンドラインのパラメータの区切りは空白文字なのは常識なので
コマンドのパラメータにコマンドを使うと、 どっちのパラメータなのか判らなくなる。
〇 コマンド (コマンド パラメータ1 パラメータ2) パラメータ3
× コマンド コマンド パラメータ1 パラメータ2 パラメータ3
だから、パラメータにコマンドの戻り値を使うなら、PowerShellでは()で括る仕様になっている。
先のEXEからアイコンを取り出すコマンドをコマンドラインから呼び出すには
ExtractionIcon "C:\Program.....\xxx.exe" (Join-Path [System.Environment]::GetFolderPath("Desktop") "sample.ico")
となってしまう。
最後に、今のPowerShellはRestrictedで、スクリプトファイルは実行不可で、
一時的に許可するなら
> pwsh.exe -ExecutionPolicy RemoteSigned xxxx.ps1
ps1のショートカットの場合も
powershell -ExecutionPolicy RemoteSigned -File ${元の内容}
となる。
ま、当然何でもできるから危なっかしい。
いくらPS1を実行不可にしても気の利いたショートカットを作られたら・・・
結局、MSが作ると、
しかし、使い物になる様にバージョンアップすると
これは定説だな。
ExcelでVBAエディタは ALT + F11 で開いていたが、
暫く振りに使ったら無反応だった。
仕様が変わったのかな?思ったら
NVIDEA GeForce Expressの中で「 ALT + F11 」をフックしていたせいだった。
普通は自分の画面上でしか 自分の ショートカットは有効にならないが
ゲームの画面をキャプチャ等をするには、
デスクトップ内で 自分の ショートカット を有効にする方法(ジャーナルだったかん?)を使うしかない。
このため「ゲーム内のオーバーレイ」で登録してある「ショートカット」は全てのアプリの 「ショートカット」 より優先されるのだ。
対処法で一番確実なのは
もし、Excelの操作をキャプチャで使ってるなら、
かな。
MDNにフォルダをドロップして中身をリストアップするサンプルを見つけた。
FireFoxではうまく動くが、ChromeやEdgeでは・・・・・・・・・
code: 0
message: "A URI supplied to the API was malformed, or the resulting Data URL has exceeded the URL length limitations for Data URLs."
name: "EncodingError"
が出てしまう。
「APIに提供されたURIの形式が正しくないか、結果のデータURLがデータURLのURL長の制限を超えています。」
ってファイル名のせい?
しかし、C:¥aaaでも同じ結果だった。
原因は判らないけど、コードをアップしてみると・・・何故かうまくいく(アップ先)。
どうやら、httpなURLなパスにあるHTMLファイルをブラウザで開いている場合はOKらしい。
微妙だ。どっちかと云えば、
得体のしれないのURLの画面のページに自分のPCのフォルダをドラッグするのは・・・
と思う。
後、先の参考URLの注釈通りにreadEntries
は、100個までしか読んでくれない。
forEachはPromiseを返さないので、非同期処理を抱えてる部分を含めて、
ローカル関数に書き換えたが forEachの最終ループで再起すると
ここは、forEachをmapにして暗黙の同期化を発動する。
ついでにmapの戻り値から readEntries が空っぽを渡たさなかった場合のみ再起するようにした。
readEntries
も非同期っぽいから、ローカル関数のままか。
フォルダに203個ファイルがあっても全部書き出してくれたから、
多分大丈夫。
メデタシ、メデタシ。(かな?
あ、HTMLのINPUTタグでwebkitdirectory を指定すると
フォルダも選択できるらしい。
こっちの方が良さそう。
でも、非標準らしい。