$p.events.after_setのブラウザによる挙動の違いについて

## はじめに
先日こんなお問い合わせがありました。
```
Q:$p.events.after_set 内で $p.apiCreate を実行したときにlocation.hrefが実行されない
```
今回は上記問合せを解決した方法と躓いた点、疑問点を備忘録代わりに残したいと思います。

## $p.events.after_set 内で $p.apiCreate を実行する
問合せの中でスクリプトが添付されていたので、まずはそちらを分解していこうと思います。
apiCreateについては[プリザンター公式のマニュアル](https://pleasanter.org/manual/script-api-create)をご確認ください。
大きく分けると3つに分解できました。
```
$p.events.after_set_Create = function (args) {
    const siteId = {
        11111: {
            Child1: 22222,
            Child2: 33333,
            Child3: 44444
        }
    };
```
```
    let response = args.json.filter(function (item, index) {
        if (item.Method == 'Response') return true;
    });

    $p.apiCreate({
        id: siteId[$p.siteId()].Child1,
        data: {
            Title: `${$p.getControl('Title').val()} - C1 - 作成`,
            ClassHash: {
                ClassA: response[0].Value,
            },
            Owner: $p.userId(),
        },
        async: false,
        done: function (result) {
            console.log(JSON.stringify(result));
        },
        fail: function (error) {
            console.log(JSON.stringify(error));
        }
    });
    console.log('end-1');
    ~~~中略~~~
    console.log('end-3');
```
```
    location.href = $p.ex.itemsUrl(response[0].Value);
```
概要としては __親テーブル(id:11111)でレコードを作成したときに、子テーブル(id:22222,33333,44444)にレコードを作るよ！__ といった内容です。

### テーブルの構成について
祖テーブル-親テーブル-子テーブル(３つ)という構成になっています。
1. 祖テーブルから親テーブルを作成
1. 親テーブルで作成ボタン押下
1. 上記スクリプトが動いて子テーブル(３つ)にレコードが作成される
という流れになっています。

これだけならlocation.hrefは記述する必要がありませんが、プリザンターの仕様では __親テーブルからレコードを作成するとリンク元に戻る__ という仕様があります。
つまり、location.hrefを記述しないと、親テーブルにて作成ボタンを押下したときに子テーブルにレコードが作成されたのを確認する前に祖テーブルに帰ってきてしまいます。

## 実際に動かしてみた
1. 祖テーブルでレコードを作成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3551590/1c9badfc-13cb-e537-f200-43c0f6f62ca5.png)

1. 祖テーブルの[親テーブルボタン]から親テーブルにレコードを作成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3551590/6c1ac42a-502b-a4e8-cb88-f9b5a1f89ce8.png)

1. 作成ボタンクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3551590/39bb272b-5554-1bfe-9f46-95542a5ea8e5.png)

ちゃんとlocation.hrefは機能しているみたいです。
今回のお問い合わせは **p.events.after_set_Create 内で $p.apiCreate を実行したときにlocation.hrefが実行されない** です。 

私の環境では実行されているようです。
実はここが落とし穴で、私の環境はGoogleChromeでした。
後ほど確認したところ、お客様の環境はMicrosoftEdgeを使用しているということでした。
私もEdgeにて確認したところ確かにlocation.hrefは機能しませんでした。

##### Edgeでの実施時
1. 祖テーブルでレコードを作成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3551590/a647f1f1-42ae-23fb-1376-c577c007ce16.png)

1. 祖テーブルの[親テーブルボタン]から親テーブルにレコードを作成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3551590/ba7452d8-532d-125c-ccb5-ff7e1a57a71c.png)

1. 作成ボタンクリック(祖テーブルに帰ってきてしまう)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3551590/dbfe3c46-f179-4f5a-9d62-dfca38883f6c.png)

初めて知ったのですが、同じスクリプトでもブラウザの仕様により動きが変わる場合があるようです。
何が違うのかといくつか記事を読んでみましたが結局原因はわかりませんでした。

:::note warn
MicrosoftEdgeはChromiumをベースとしているため、基本的な動作はchromeと同じ認識です。
有識者の方、もし何かご存知でしたら教えていただけると非常に助かります！！
:::

とはいえ解決策を考えないといけないので、別の方法を考えます。

## 調査
スクリプトでは各レコード作成後にコンソールにログが出るようになっていたので確認します。

##### Chrome
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3551590/e14bbbb6-0fd1-32ef-dbec-5ac8bd7ec856.png)

##### Edge
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3551590/fa2c5ddc-df0a-da43-0c06-08a9c66146f5.png)

ログが出る前に戻ってしまっていることが分かりました。

また、作成した親テーブルに戻ったところレコードは正しく作成されていることが分かりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3551590/121552cd-f9fb-cdff-bd3c-0332038983c6.png)

以上のことから、p.apiCreateは問題なさそうですが、どうやらlocation.href = $p.ex.itemsUrl(response[0].Value);がうまく動いていないのではないかと推定します。

## Edgeでも動くようにしてみた
最初は非同期処理のタイミングの問題かな？と思い、async/awaitを使用してタイミングをずらしてみましたが失敗。
元のスクリプトではapiCreateを３つ使用していたので、１つにまとめて処理をするようにしてみました。
```
function createApiRequest(childIdentifier) {
    $p.apiCreate({
        id: siteId[$p.siteId()][childIdentifier],
        data: {
            Title: `${$p.getControl('Title').val()} - ${childIdentifier} - 作成`,
            ClassHash: {
                ClassA: response[0].Value,
            },
            Owner: $p.userId(),
        },
        async: false,
        done: function (result) {
            console.log(JSON.stringify(result));
        },
        fail: function (error) {
            console.log(JSON.stringify(error));
        }
    });
    console.log(`end-${childIdentifier}`);
}

// 使用例:
createApiRequest('Child1');
createApiRequest('Child2');
createApiRequest('Child3');
```
これも失敗。一向に解決しませんでした。

そこでp.events.after_setの中で全部の処理をしているのが悪いのでは？というアドバイスを頂き、もっと前の段階で処理のタイミングを変えてみることにしました。

1. $p.events.after_set_Create からlocation.hrefの行を削除
1. function名を、p.events.after_set_Createから$p.events.before_set_Create に変更
    ```
    $p.events.before_set_Create = function (args) {
        const siteId = {
        11111: {
            Child1: 22222,
            Child2: 33333,
            Child3: 44444
        }
    };
    ~~~~~中略~~~~~
            done: function (result) {
            console.log(JSON.stringify(result));
        },
        fail: function (error) {
            console.log(JSON.stringify(error));
        }
    });
    console.log('end-3');
    ```

1. 新たにp.events.after_set_Createを作成し、その中でlocation.href = $p.ex.itemsUrl(response[0].Value);を動かします。
    ```
    $p.events.after_set_Create = function (args) {
    let response = args.json.filter(function (item, index) {
        if (item.Method == 'Response') return true;
    });    
    location.href = $p.ex.itemsUrl(response[0].Value);
    }
    ```

##### 結果
1. 祖テーブルでレコードを作成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3551590/5e767f0b-a2ee-1604-e507-1f61ac4eedb8.png)

1. 祖テーブルの[親テーブルボタン]から親テーブルにレコードを作成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3551590/cd86deac-4b75-6230-faec-1c7ba605d26a.png)

1. 作成ボタンクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3551590/b121aaa2-8f2f-2212-d48e-1ad9b84d92dd.png)

Edgeでも問題なく動くことが確認できました！！

## $p.events.before_setと$p.events.after_set
さて、今回は処理のタイミングを分けるため、もともとafterのみだった処理を、beforeとafterで分割しました。実際どのような処理が行われているのでしょうか。
[開発者向け機能：スクリプト | Pleasanter](https://pleasanter.org/manual/script)プリザンターのマニュアルからの引用になりますが、大きく分けて4つのタイミングがあります。
|関数名|タイミング|詳細|
|---|---|---|
|$p.events.before_send|データ送信前|サーバへデータを送信する前に実行|
|$p.events.after_send|データ送信後|サーバへデータを送信した後に実行|
|$p.events.before_set|画面の更新前|サーバへデータを送信後、画面内容を更新する前に実行|
|$p.events.after_set|画面の更新後|サーバへデータを送信後、画面内容を更新した後に実行|

今回実施したのは上から３つ目４つ目のbefore_setとafter_setです。
もともとafter_set(サーバへデータを送信後、画面内容を更新した後に実行)で行われていましたが、
レコードの作成はデータ送信後、画面が切り替わらなくても実行して問題ありません。(データ送信前はNG(データがない為))
しかし、location.hrefは画面更新後でないといけません。
これは画面更新前にlocation.hrefが動いてもその後の画面更新によって戻って来てしまうためです。
というわけでapiCreateはbefore_setで実行、そしてlocation.hrefはafter_setで実行と分けたところうまくいったという顛末となりました。

## おわりに
私はまだまだプログラミングもプリザンター勉強中の身です。
今回のようなサポート対応は定期的に対応するのですが、その都度あーでもないこーでもない、と頭を抱えることが多いです。
特に今回は「ブラウザによって処理タイミングが変わる」という貴重な体験をしました。
結局原因はわかりませんでしたが、解決策を考えることはできたので個人的には及第点かなと考えています。
今後もこのような備忘録兼仕様について為になるような記事を残していこうと思います。