2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Promiseの使い方

Last updated at Posted at 2022-12-21

Promise

Promiseを使うときに、通常にnewしてから使います。

つまり、Promiseはコンストラクターです。では、console.dir(Promise)でPromiseの中身を見てみましょう!

スクリーンショット 2022-12-21 11.40.42.png

 Promiseの中にある、resolve, reject, catch, thenメソッドについてまだ分からないと思いますが、順を追って説明いたします。
◉また余談ですが、Promiseは英語で約束の意味があります。一定時間後に必ず結果(result)を返してくるねという風に覚えてればいいと思います。

まずPromiseの重要な属性、3つの状態から説明いたします。

とても大事な内容となるので、ぜひ理解してください。
let a1 = new Promise((resolve)=>{
  setTimeout(()=>{
    resolve(1)
  },2000);
})

console.log(a1);

consoleから確認できるように、2秒後Promiseの状態(PromiseState属性)がpendingからfullfilledに変わりました。
スクリーンショット 2022-12-21 12.06.57.png
スクリーンショット 2022-12-21 13.18.32.png

pending, fullfilled, rejectedはそれぞれ下記の意味を表しています。
pending: 初期状態
● fullfilled: 処理が成功しました
● rejected: 処理が失敗しました

次に、Promise関数定義の仕方を見ていきましょう。
1のアロー関数の書き方が現在主流になっています。

Promiseの関数定義

// 書き方1
let a1 = new Promise((resolve, reject)=>{

})

// 書き方2
let a2 = new Promise(function(resolve, reject){

})

 resolveとrejectメソッドは引数となります。他の名前にすることもできますが、一般的にそのままresolveとrejectを使うのが普通です。また他のプログラミング言語では、funciton(メソッド)を引数として渡せない場合が多いですが、javascriptができます。これもjavasript言語の魅力の一つです!

そのために、functionブロックにあるresolve(1)とはresloveメソッドに引数1を渡す意味となります。そして、もう一点覚えてほしいことは、Promiseの状態が一旦変われば、再度変更されることはありません。


例えば、pendingからfullfilledにchangeしたら、またrejectの状態に遷移することはありません。
下記の関数では、resolveメソッドが実行されると、rejectメソッドは実行しません。
resove, reject両者の中で一つのみしか実行されません。

let a1 = new Promise((resolve, reject)=>{
  resolve();
  reject();  // 実行されません
})

console.log(a1);

下記関数の実行結果を見てみましょう。

let a1 = new Promise((resolve, reject)=>{
  resolve(1);
  reject();
})

console.log(a1);

スクリーンショット 2022-12-21 13.46.17.png
結果から分かるように、PromiseResultの結果は1です。では、どのようにすればPromiseResultの値を取得することができるのでしょうか。
答えはthenメソッドとcatchメソッドを使うことです。thenとcatch二つともcallback関数です。
resolve関数の処理が成功されると、PromiseResultがthenメソッドに渡されます。
● reject関数の処理が成功されると、PromiseResultがcatchメソッドに渡されます。

new Promise((resolve,reject)=>{
  resolve(123)
}).then(res=>console.log(res + '成功したら実行される'))
  .catch(error=>console.log(err + '失敗したら実行される'))
  .finally(()=>console.log('成功や失敗のどちらでも実行される'));

また、catchを省略し、下記のように書くこともできます。then関数に二つの引数を渡すという書き方です。

new Promise((resolve,reject)=>{
  reject(123)
}).then((res)=>console.log(res + '成功'),
        (err)=>console.log(err + '失敗'));

catch, then, resolve, rejectについて紹介はここまでにしたいと思います。次にPromise chainの概念について書きます。
まず前提として分かってほしいのは.then関数が処理された後の戻り値は新しいPromise対象です。

let a1 = new Promise((resolve,reject)=>{
  resolve('123')
});

let a2 = a1.then((res)=>{}); 

console.log(a2);  //undfined
console.log(a1 === a2);  // false

処理結果はこんな感じです!
スクリーンショット 2022-12-21 14.07.47.png
a1.thenの処理結果はundefinedなのに、なんでPromiseStateはfullfilledと疑問に思う方はいると思いますが、原因としては、reject関数が呼ばれていないため、PromiseStateはrejectに変更しません。resove関数について、例え結果はundefinedでもPromiseStateはfullfilledです。

例えば、下記の例だとPromiseStateはrejectになります。

let a1 = new Promise((resolve,reject)=>{
  resolve('123')
});

let a2 = a1.then(()=>{  
    let a3 = new Promise((resolve,reject)=>{
      reject()
    })
    return a3
})

console.log(a2);

処理結果:
スクリーンショット 2022-12-21 14.26.56.png

では、Promise chainの使い方を見ていきましょう。

let a1 = new Promise((resolve, reject)=>{
  resolve(1)
})
let a2 = a1.then((res)=>{
  console.log(res); //1
  return 2
})
let a3 = a2.then((res)=>{
  console.log(res); //2
  return 3
})
let a4 = a3.then((res)=>{
  console.log(res); //3
})

もちろん変数名を省略することもできます。

let a1 = new Promise((resolve, reject)=>{
  resolve(1)
}).then((res)=>{
  console.log(res); //1
  return 2
}).then((res)=>{
  console.log(res); //2
  return 3
}).then((res)=>{
  console.log(res); //3
})

では、Promiseを使うことで、実際にどんな問題が解決できるのでしょうか。
はい、答えはcallbackの無限ループ問題を解決できます。
下記のコードを見ていきましょう。

function fn(a){
  setTimeout(()=>{
    console.log(a);
    a--;
    setTimeout(()=>{
      console.log(a);
      a--;
      setTimeout(()=>{
        console.log(a);
        a--;
        setTimeout(()=>{
          console.log(a);
          a--;
          setTimeout(()=>{
            console.log(a);
            a--;
          },1000)
        },1000)
      },1000)
    },1000)
  },1000)
}

fn(6);

コードから分かるように、毎回前回処理結果の値を使って新規の処理を行いますが、ソードコードがとても読みづらく、保守性も低いです。
Promiseを使えば、ソースコードは下記に変身できます!

function fn(a) {
    new Promise((resolve,reject) =>{
      resolve(a)
    })
    .then((params)=>{
    return new Promise((resolve, reject)=>{
      setTimeout(()=>{console.log(params); resolve(--params)},1000)})
    })  
    .then((params)=>{
      return new Promise((resolve,reject)=>{
        setTimeout(()=>{console.log(params); resolve(--params)},1000)})
    })    
    .then((params)=>{
      return new Promise((resolve,reject)=>{
        setTimeout(()=>{console.log(params); resolve(--params)},1000)})
    })    
    .then((params)=>{
      return new Promise((resolve,reject)=>{
        setTimeout(()=>{console.log(params); resolve(--params)},1000)})
    })    
    .then((params)=>{
      return new Promise((resolve,reject)=>{
        setTimeout(()=>{console.log(params); resolve(--params)},1000)})
    })
  }

fn(6)

はい、Promiseを利用すればソースコードの可読性がだいぶ向上しましたね!

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?