MENU

JavaScript同期・非同期

【JavaScript】 非同期はPromise?解説が難しいので自分で理解してみた

更新日:2021/07/16

 

Promiseの概要

 

 

Promiseについて説明しようとすると難しくなるので、まずは実際の仕事におきかえてみます。

 

と思ったのですが、意味が分からないと不評なので非表示にしました。
意味が分からないけれど読んでみたいという方は、開くをクリックしてください。

 

 

この仕事、今すぐでなくてもいいんだよな…

 

他の仕事もあるし…

 

そうだ、外部の業者に丸投げしよう!!


 

 

僕が報告を受けるのも面倒だから、受付担当を作っておこうかな…


 

resolve  reject

 

そしてお仕事を発注します

 

 

 

では、この内容でお仕事お願いしますね

 


 

 

 

受けたわまりました!

 

で、納期はいつまでに・・・?

 


 

 

 

特にないので終わったらでいいですよー

 

終わったら受付の resolveさんに報告してくださいねー

 

何か問題があったら rejectさんにお願いしますー

 


 

 

 

わかりました!

 


 

 

 

ではそのような約束(Promise)でお願いしますー

 


 

そして・・・

 

 

急ぎの仕事だけやっていればいいから、はかどるなー


 

つまりPromiseは、処理を他に丸投げして、自分の処理を続行できる機能です。

 

 

非同期処理とは

 

JavaScriptでの非同期処理とは、一連の処理の流れで、後に続く処理に結果を引き継ぐ必要がない処理を後回しにし、処理を続行させることを指します。

 

または、時間のかかる処理と、その結果についての後処理を後回しにし、それ以外の処理を続行させることを指します。

 

正確な意味では、後回しにした処理が非同期処理です。

 

ただし、非同期処理は並列処理ではありません。

 

次のコードは、非同期処理の例です。

 

非同期処理の例

 


const start = Date.now();
const callBack = ()=> {
            console.log(Date.now() - start);
};

setTimeout(callBack, 3000 );

for( let i = 0n ; i < 999999999n ; i ++ );

 

setTimeoutでタイマーセット後、タイマー終了を待たずに次のforループが実行されます。
そして3秒後ぐらいに関数callBackが非同期で実行されます。

 

しかし僕のパソコンで実行すると、約9秒後にcallBackが実行されました。

 

これは、非常に時間がかかるforループの処理が終わった後に、3秒経過のチェックが行われcallBackが実行されたためです。

 

並列処理なら、forループ中にcallBackが実行されるので、並列処理ではないことがわかります。

Promise オブジェクトとは

 

 

Promiseは、次のような二つの引数を持つ関数をメインの処理から独立して実行できるオブジェクトです。

 

function a( 引数1 , 引数2 ) {  }

 

promiseはメイン処理とは非同期で動作

 

関数が短かければ一瞬で終わります。
ですがボタン操作などの入力を待っていたりすると非同期で動いているんだと実感できます。

 

 

それはいいとして...

 

関数の実行結果をメイン処理で受け取ることはできません。

 

そのかわり二つの引数を使用して通知します。
通常Promiseで使用する関数の引数は、次のような名前で定義されます。

 

function a( resolve , reject ) {  }
    resolve : 成功時に呼び出すメソッド
    reject : 失敗時に呼び出すメソッド

 

引数は共にメソッドです。
次の例のように( )で呼び出して使用します。

 

50%の確率で成功する関数


function a( resolve , reject ){
      let per = Math.floor( Math.random() * 100 );
      (per >= 50 ) ? resolve(per) : reject(per);
}

 

resolve()reject()は、Promiseオブジェクトが用意した内部メソッドです。
Promise.prototype.thenメソッドで登録した後処理(コールバック関数)への橋渡しをおこないます。

Promiseの使い方

 

Promiseを使うには、まずPromiseコンストラクターでオブジェクトを作成します。

 

コンストラクターって何?という人は、下の参考を見てね!
参考:【JavaScript】 コンストラクターとは?関数とは違うのか?

 

 

Promiseオブジェクトの作成

 

Promiseオブジェクトは、Promiseコンストラクターに非同期で実行したい関数を渡して作成します。

 

Promiseオブジェクト作成例


  //50%の確率で成功する関数
function a( resolve , reject ){
    let per = Math.floor( Math.random() * 100 );
    (per >= 50 ) ? resolve(per) : reject(per);
}

  // 関数aを処理するPromiseオブジェクトを作成
const prm = new Promise( a );

 

引数のresolverejectは、JavaScriptのシステムが割り付けた関数です。

 

非同期処理の結果が出たら、引数に結果の値を指定してresolveを実行します。

 

ただし、エラーなどで失敗した場合は、rejectを呼び出します。

 

ここではresolverejectの両方を呼び出す例として、上記のようなコードを作成しています。
しかしあまり良い例とは言えません。

 

rejectは、通信エラーなど、正常な処理をおこえなかったときに呼び出します。

 

上のコードは、直接関数式を渡す形式で書くことができます。

 

Promiseオブジェクト作成例2

 


const prm = new Promise(
    function ( resolve , reject ){
        let per = Math.floor( Math.random() * 100 );
        (per >= 50 ) ? resolve(per) : reject(per);
   }
);

 

 

functionではなく、アロー関数でも書けます。

 

Promiseオブジェクト作成例3

 


const prm = new Promise(
   ( resolve , reject ) => {
        let per = Math.floor( Math.random() * 100 );
        (per >= 50 ) ? resolve(per) : reject(per);
   }
);

 

クロージャを使用して、確率を変更できるように関数化してみます。

 

Promiseオブジェクト作成例4

 


function func1( kakuritu ) {
   return new Promise( // Promiseを返す
      ( resolve , reject ) => {
           let per = Math.floor( Math.random() * 100 );
           (per >= kakuritu ) ? resolve(per) : reject(per);
      }
   );
};

const prm = func1( 30 ); // 30%でPromiseオブジェクトを作成

 

上記のような関数は、『Promiseを返す関数』と表現されることがあります。

 

後処理関数登録:Promise.prototype.then

 

次にresolve()reject()で呼び出される関数を登録してみます。

 

登録は、Promiseのプロトタイプオブジェクトに設定されているthen()メソッドPromise.prototype.thenを使用します。

 

構文:

Promise.prototype.then( resolve_func ,  reject_func )
    resolve_func : resolve()から呼び出される関数
    reject_func : reject()から呼び出される関数。省略可能

 

仕様上は引数名が onFulfilledonRejectedになっていますが、わかりにくいのでresolve_funcreject_funcにしてあります。

 

Promise.prototype.then使用例

 


function a( resolve , reject ){
    let per = Math.floor( Math.random() * 100 );
    (per >= 50 ) ? resolve(per) : reject(per); // (1)
}
function resolve_func( p ){ console.log( "勝ち:" + p ); } // 勝ちコールバック関数
function reject_func( p ){ console.log( "負け:" + p ); } // 負けコールバック関数

new Promise( a ).then( resolve_func , reject_func );

 

 

 

上の例のように、各コールバック関数(resolve_func,reject_func)は引数を一つ持ちます。

 

各コールバック関数の引数の値は、非同期処理関数でresolve()またはreject()を呼び出す際に、引数で与えた値です。

 

上の例では関数a内の(1)の変数perの値が、コールバック関数に渡されます。

 

(per >= 50 ) ? resolve(per) : reject(per); // (1)

 

→resolve(per)のとき、resolve_func(per )が呼ばれる
→reject(per)のとき、reject_func(per )が呼ばれる

 

補足:

 

次のコードは、PromiseコンストラクターでPromiseインスタンスを作成し、そのインスタンスに対してthenメソッドを実行しています。

 

new Promise( a ).then( resolve_func , reject_func );

 

これは、次のように記述できます。

 

const prm = new Promise( a );
prm.then( resolve_func , reject_func );

 

 

 

例外が発生した場合

 

非同期関数内で例外が発生した場合、reject_funcが呼び出されます。

 


function a( resolve , reject ){
    throw new Error("****error!****"); // 例外をスロー
}

new Promise(a)
         .then( ()=>{} , e=>console.log("error:" , e) ); // error:Error: "****error!****"

 

 

 

 

 

【失敗関数のみ登録】:Promise.prototype.catch

 

Promise.prototype.catch()メソッドは、非同期関数内でreject()呼び出しまたは、例外が発生したときに呼び出されるコールバック関数を登録します。

 


function a( resolve , reject ){
    throw new Error("****error!****"); // 強制的に例外を発生
}

new Promise(a)
            .catch(
                e=>console.log("catch:" , e) // catch: Error: "****error!****"
            );

 

catch()の前に2番目の引数を指定たthen()が実行されている場合は、then()が優先されます。

 


function a( resolve , reject ){
    throw new Error("eror!"); // 強制的に例外を発生
}

new Promise(a)
            .then( ()=>{} , e=>console.log("error:" , e)  )
            .catch(
                e=>console.log("catch:" , e)
            );

 

 

 

2番目の引数が無ければ、catch()が有効になります。

 


function a( resolve , reject ){
    throw new Error("eror!"); // 強制的に例外を発生
}

new Promise(a)
            .then( ()=>{} )
            .catch(// こちらが呼び出される
                e=>console.log("catch:" , e)
            );

 

 

 

【後処理関数登録】:Promise.prototype.finally

 

ここまでの話の流れから、少し混乱するかもしれませんが、ここでの後処理は、成功・失敗関係なく呼び出される処理です。

 

Promise.prototype.finallyは、非同期関数が成功でも失敗でも、呼び出されるコールバック関数を登録します。

 


function a( resolve , reject ){
    let per = Math.floor( Math.random() * 100 );
    return (per >= 50 ) ? resolve(per) : reject(per);
}

new Promise(a)
        .then( e => console.log("あなたの勝ち") )
        .catch( e => console.log("あなたの負け") )
        .finally( () => console.log("勝負終了") );

 

 

 

このコードを実行すると、『あなたの勝ち』または『あなたの負け』と表示した後に『勝負終了』と表示されます。

 

Promise.prototype.finallyで登録するコールバック関数には、引数を指定できません。
そのため、関数が成功したかどうかを引数から確認することができません。

 

あくまで、後処理に使用するのが目的です。

 

Promiseでの処理で大きなデータを使用している場合、そのまま終了するとデータが残ってしまうことがあります。
変数にnullなどを代入すると開放してくれるので、Promise.prototype.finallyを活用してください。

 

Promise.prototype.finallyは結果をスルーする

 

Promise.prototype.finallyは、thenやcatchで結果を受け取っていない場合、そのまま次に渡します。

 

 

意味が分かりません


 

説明が難しいので例を挙げます。

 


function a( resolve , reject ){
    let per = Math.floor( Math.random() * 100 );
    return (per >= 50 ) ? resolve(per) : reject(per);
}
new Promise(a)
        .finally( () => console.log("勝負終了") )
        .then( e => console.log("あなたは勝ちました(" + e + ")" ) )
        .catch( e => console.log("あなたは負けました(" + e + ")") );

 

 

 

このコードを実行すると、『勝負終了』と表示された後に『あなたの勝ち(数値)』または『あなたの負け(数値)』と表示されます。
数値は、関数aでresolve(per)またはreject(per)したときのperです。

 

これはどういうことかというと、関数aの結果に対してfinally()は何も処理をしていないということです。
そして、もしthen()catch()が後に続くなら、そちらに結果を渡しているのです。

 

次の例は、この現象の補足です。

 


function a( resolve , reject ){
    let per = Math.floor( Math.random() * 100 );
    return (per >= 50 ) ? resolve(per) : reject(per);
}

new Promise(a)
        .then( e => console.log("あなたは勝ちました(" + e + ")" ) )
        .catch( e => console.log("あなたは負けました(" + e + ")") )
        .finally( () => console.log("勝負終了") )
        .then( e => console.log("あなたは勝ちました(" + e + ")" ) )
        .catch( e => console.log("あなたは負けました(" + e + ")") );

 

 

 

上の例はfinally()の前後にthen()catch()が記述されています。
しかし後ろのthen()catch()は、undefinedと表示されます。

 

これはfinally()の前のthen()catch()が非同期関数aの結果を処理した後、結果にconsole.log()の戻り値undefinedをセットしています。

 

そしてfinally()は何もせずにundefinedをスルー。
その後、then()が、undefinedを捕捉しているのです。

 

説明がよくわからないかもしれません…

 

これはPromiseチェーンという仕組みです。
Promiseチェーンはこの記事の後の方で紹介していますので、そちらを見てください。

Promiseオブジェクトのタイミング

 

Promiseオブジェクトで処理する関数は、メインの処理と独立して動作します。

 

と言われて気になるのが、いつ動作するのか。
つまりタイミングですね。

 

正確なことはJavaScriptの仕様書(ECMAScript)を解読する必要がありますが、ここでは実際の動作で確認してみます。

 

Promiseコンストラクターのタイミング

 

英語が苦手な僕がECMAScriptを解読した限りでは、Promiseコンストラクターに渡された関数はその場で実行されています。
次のコードで確認してみます。

 

Promiseで渡された関数のタイミング確認

 


console.log( "(1)start" , tim( ));

new Promise( function () {
            console.log( "(2)Promise start" , tim( ));
            setTimeout(()=>{
                console.log( "(4)Wait end" , tim( ));
            }, 3000)
        });

console.log( "(3)end" , tim( ));

 

 

 

tim()は現在の時刻を表示していて、別途次のようなコードを用意しています。

 

現在の時刻を表示


const tim = () =>{const d = new Date;return d.getHours().toString() + ':' + d.getMinutes().toString() + ':'+ d.getSeconds().toString()+ ':'+ d.getMilliseconds().toString();};

 

もし、非同期関数を後で実行するなら、次のようになるはずです。

 

(1)start 16:55:24:760
(3)end 16:55:24:762
(2)Promise start 16:55:24:762
(4)Wait end 16:55:27:764

 

しかし、実際には次のようになりました。

 

実行結果:

(1)start 16:55:24:760
(2)Promise start 16:55:24:762
(3)end 16:55:24:762
(4)Wait end 16:55:27:764

 

つまり、Promiseコンストラクターがオブジェクト生成する時に、非同期関数が実行されます。

 

 

 

Promise.prototype.thenのタイミング

 

Promise.prototype.thenは、非同期関数の後処理関数を登録するメソッドです。

 

ではthenで登録する前に、関数の処理が終わってしまったらどうなるのでしょうか。

 

次のコードは3秒後にresolveを呼び出します。
そして、6秒後にthen()を実行しています。

 

つまり結果が出てから、結果を処理する関数を登録したことになります。

 


console.log( "start" , tim( ));
const a = new Promise( function ( resolve ) {
            setTimeout(()=>{ // 3秒後にresolve()を呼び出す
                console.log( "call resolve()" , tim( ));
                resolve(1);
            }, 3000);
        });

setTimeout(()=>{ // 6秒後に resolve()で呼び出すコールバックセット
            console.log( "set .then" , tim( ));
                 // thenを登録
            a.then( e => console.log("success:" + e),
                e => console.log("error:" + e));
        }, 6000);

 

 

 

実行結果:

call resolve() 18:28:22:848
set .then 18:28:25:839
success:1

 

非常に遅いタイミングでthen()を実行したのにもかかわらず、問題なくコールバック関数が呼び出されました。

 

resolve()reject()が呼ばれても、thenが実行されるまで待ってくれるのです。

 

これはcatch()やfinally()でも同じです。

Promiseは非同期ではない

 

ここまでの説明で気が付いている方もいると思いますが、Promiseは非同期ではありません。
同期で実行されます。

 

 

なんだってー!!


 

Promiseは非同期であるという大前提を否定しています。
騙された気分ですね。

 

次の例は非同期のように見えます。

 

 


console.log("Start");

new Promise(   resolve  => {
                    setTimeout( ()=> {
                       console.log("Wait End");
                       resolve(1);
                      }, 3000 );
                 })
         .then( () => console.log("Call Then"));

console.log("End");

 

 

 

結果:

Start
End
Wait End
Call Then

 

メインの処理終了後に、Promiseに渡した関数の結果が表示されているので、非同期のような気がしますね。

 

では次の例はどうなるのでしょうか。

 

 


console.log("Start");
new Promise( resolve => {

            const st = new Date().getTime();
            while( new Date().getTime() - st < 3000  ) { }

            console.log("Wait End");
            resolve(1);
        }).then( () => console.log("Call Then"));

console.log("End");

 

 

 

先ほどはsetTimeout()で3秒待ちましたが、今回は3秒経過するまでループで待っています。

 

結果:

Start
Wait End
End
Call Then

 

ループが終わるまで、メインの処理に戻っていませんね。

 

流れとして次のようになります。

 

メインの処理 → 渡した関数の処理 → メインの処理

 

しっかりと同期で処理が進んでいます。
非同期なんてどこにもありません。

 

ではなぜ最初の例が非同期に見えるのかというと、元々非同期処理であるsetTimeout()にresolve()reject()を処理するコールバック関数を渡して、後の処理を丸投げしているからです。

 

DOMのクリックなどのイベントや、HTTP通信などでPromiseを使用した場合も同じですね。

 

ではPromiseは、実際には何なのでしょうか。

 

Promiseは、setTimeoutやイベントリスナーなどの非同期処理に、後処理を追加する手段の一つです。

 

Promiseそのものは、関数を非同期で実行するような機能はありません。

 

 

Promiseチェーン

 

Promiseオブジェクトには、Promiseチェーンという機能があります。

 

次のようにthen()catch()finally()を連結したものが、Promiseチェーンです。

 

new Promise(  ).then().then().then().catch().finally();

 

ウソです。

 

.then().then()はメソッドチェーン

 

上の例はPromiseチェーンを作成する一要素ではありますが、本質的には単なるメソッドチェーンです。
しかし上の例をPromiseチェーンそのものとして紹介しているケースがあって、よく理解してなかった当時の僕はいろいろ混乱しました。

 

then()メソッドは、Promiseオブジェクト内の待ち行列にコールバック関数を登録します。

 

Promiseオブジェクト then() 呼び出し

 

 

.then()の結果でthen()を処理

 

Promiseチェーンは、Promiseオブジェクトの内部的な仕組みです。

 

Promiseのインスタンスは次のような形式で作成し、そのインスタンスからthen()を呼び出して関数を待ち行列に登録します。

 

new  Promise( 関数P ).then(関数).then(関数) …

 

Promiseの引数として渡した関数P内でresolve()またはreject()が呼ばれると、待ち行列の最初のコールバック関数が実行されます。

 

Promise チェーン resolve

 

実行されたコールバック関数は、必ずPromiseオブジェクトをリターンします。
コード上でリターンしていない場合は、undefinedが適用されています。

 

リターンしたPromiseオブジェクトがDOMイベント待ちなどの場合は、その結果が出るのを待ち、次の待ち行列を処理します。

 

Promise チェーン リターン

 

このように、then()やcatch()で登録した関数がPromiseオブジェクトをリターンし、その結果によって次の関数を実行する流れがPromiseチェーンです。

 

次のコードは、Promiseチェーンを利用して「はい(OK)」「いいえ(キャンセル)」で答えられる簡単なアンケートの例です。

 

Promiseチェーンの例

 


function promiseNew( e ) {
        return new Promise( ( resolve,reject  ) => {
            confirm(e) ? resolve(1) : reject(1);
        });
    }
promiseNew("お腹がすきましたか?")
        .then( e =>  promiseNew("パスタでいいですか?"),
                e =>  promiseNew("スイーツを食べましょう!"))
        .then( e =>  alert("では行きましょう!"),
            e =>  alert("明日にしよう..."));

 

 

Promiseチェーンでプリミティブやオブジェクトをリターンする

 

次のようなコードがあるとします。

 

 


new Promise( ( resolve  ) => resolve(1) )
        .then( e => {console.log("then1:",e ); return 2; })
        .then( e => {console.log("then2:",e ); return 3; } )
        .then( e => console.log("then3:",e ) );

 

 

 

結果:

then1: 1
then2: 2
then3: 3

 

上の例では、Promiseオブジェクトではなくて数値プリミティブをリターンしています。

 

Promiseオブジェクト以外の値がリターンされると、Promise.resolve()に渡されます。
つまり、イメージとしては次のようなコードになります。

 

new Promise( ( resolve  ) => resolve(1) )
        .then( e => {console.log("then1:",e ); return Promise.resolve( 2 ); })
        .then( e => {console.log("then2:",e ); return Promise.resolve( 3 ); })
        .then( e => console.log("then3:",e ));

 

Promise.resolve()は実行結果が必ずresolveになり、次のthen()で登録した関数が実行されます。

 

Promiseチェーンで何もリターンしない

 

関数で戻り値を指定しないと、結果はundefinedとなります。

 


function a(){}
console.log( a() ); // undefined

 

thenで登録した関数で何もリターンしない場合も同様で、undefinedがリターンされたことになり、その値がPromise.resolve()に渡されます。

 

 


new Promise( ( resolve  ) => resolve(1) )
        .then( e => console.log( "then1:" ,e ) )
        .then( e => console.log( "then2:" ,e ) )
        .then( e => console.log( "then3:" ,e ) );

 

 

 

結果:

then1: 1
then2: undefined
then3: undefined

 

暗黙的に次のようなコードと同等です。

 

new Promise( ( resolve  ) => resolve(1) )
        .then( e => {console.log("then1:",e ); return Promise.resolve( undefined ); })
        .then( e => {console.log("then2:",e ); return Promise.resolve( undefined ); })
        .then( e => {console.log("then3:",e ); return Promise.resolve( undefined ); })

 

実際には最後のthenもundefinedを返しています。
そのため、延々とPromiseチェーンを続けることができます。

 

Promiseチェーンで例外をスローする

 

関数内で例外をスローすると、then()の2番目またはcatch()で登録した関数が実行されます。

 

 


new Promise( ( resolve  ) => resolve(1) )
        .then( e => {
            console.log("then1:",e );
            throw new Error("then1Error"); // (1)
        })
        .then( e => {console.log("then2:",e );return 3;} ,
            e => console.log("error:",e.message ) // (1)を捕捉
        )
        .then( e => console.log("then3:",e ) );

 

 

 

結果:

then1: 1
error: then1Error
then3: undefined

 

最初のthen()でErrorオブジェクトをスローしています。
その結果、2番目のthen()の二つ目の関数が呼び出されていますが、この中で何もリターンしていないため、3番目のthen()はundefinedを受け取っています。

 

catch()のあとにthen()があるとチェーンする

 

catch()は終了を意味しません。

 

catch()のあとにthen()catch()finally()があると、コールバックが実行されます。

 

 


new Promise( ( resolve  ) => resolve(1) )
        .then( e => {
            console.log("then1:",e );
            throw new Error("then1Error");
        })
        .then( e => {console.log("then2:",e );return 3;})
        .then( e => {console.log("then3:",e );return 4;} )
        .catch(e => console.log("catch:",e.message ))
        .then( e => {console.log("then4:",e );return 5;});

 

 

 

結果:

then1: 1
catch: then1Error
then4: undefined

 

どこまでも続くのね…


 

上の例ではthen4undefinedですが、catch()で登録しているコールバックで値をリターンすると、then4undefinedでなくなります。

 

例:
.catch(e => {console.log("catch:",e.message );return 1;})

 

catch()は、『Promiseチェーンの最後につけてエラーを処理するためのメソッド』のように説明されていることが多いです。
しかし本質的には、reject()に対応するコールバックのみ登録できるthen()でしかありません。

まとめ

 

今回はPromiseについて調べてみました。

 

日本語にすると約束です。
つまり約束なオブジェクトです。
ますます意味がわからなくなりました。

 

たぶんプロミスと聞くと過去を思い出して、苦手って思う日本人が多いんじゃないかと思います。

 

Promiseは理解するととても単純です。
悩みすぎに注意しましょう。

けーちゃんおススメJavaScript入門書

  • スラスラ読める JavaScript ふりがなプログラミング
  • プログラム未経験者がJavaScript始めるならコレ!
    コードを掲載して自分で理解しろという投げっぱなしな入門書とは異なり、コードに一つ一つどんなことをやっているかをふりがなという形式で解説しています。
    それでいてJavaScriptの基礎と応用を学べる良書です。
  • これからWebをはじめる人のHTML&CSS、JavaScriptのきほんのきほん
  • JavaScriptの機能を実践で活かすにはHTMLやCSSの知識が不可欠です。
    しかしそれらの知識があることが前提として書かれている書籍が多い中、この本は総合的な知識を身に着けることができます。
    HTMLやCSSの知識も不安な方には、ぴったりの一冊です
  •  

    入門書の役割は、自分のやりたいことをネットで調べることができるようになるための、基礎的な知識の獲得です。
    まずはこれらの本でしっかりと基礎知識を身につけましょう。
    そしてもっと高度なことや専門的なことはネットで調べ、情報が足りないと感じたら書籍を購入してください。


    期間限定情報:
    7/16から7/18は63時間のビッグセール!
    欲しかったアレが安く手に入るチャンスです
    忘れずにチェックしてください!
    僕は以前のタイムセール祭りで4Kモニタが買ったけど、それより安かったらどうしよう・・・

    さらにお得なポイントアップキャンペーンも同時開催!

    記事の内容について

     

    こんにちはけーちゃんです。
    説明するのって難しいですね。


    「なんか言ってることおかしくない?」
    たぶん、こんなご意見あると思います。

    裏付けを取りながら記事を作成していますが、僕の勘違いだったり、そもそも情報源の内容が間違えていたりで、正確でないことが多いと思います。

    そんなときは、ご意見もらえたら嬉しいです。

    ご意見はこちら。
    https://note.affi-sapo-sv.com/info.php

    【お願い】

    お願い

    ■このページのURL


    ■このページのタイトル


    ■リンクタグ


    ※リンクして頂いた方でご希望者には貴サイトの紹介記事を作成してリンクを設置します。
    サイト上部の問い合わせよりご連絡ください。
    ただしサイトのジャンルによっては、お断りさせていただくことがあります。