MENUCLOSE
twitter ツイッターfeedlyRSS

オンスタでは、オンラインで学習するために役立つコンテンツを掲載しています。そのほか、UdemyをはじめとしたEラーニングサービスの紹介も行っています。

オンスタ | オンラインで学習するための近道になるサイト
[JavaScript_中級者への道] Js_Promiseを使ってみる

[JavaScript_中級者への道] Js_Promiseを使ってみる

公開日:2020年02月21日
最終更新日:2020年02月21日
オンスタ運営
プログラミング
ホーム > 全記事一覧 > 制作 > エンジニア > プログラミング > [JavaScript_中級者への道] Js_Promiseを使ってみる
このエントリーをはてなブックマークに追加
[JavaScript_中級者への道] Js_Promiseを使ってみる
【PR】Udemyならスマホでもタブレットでも、いつでも気軽に学べます。

Promiseとは

Promise は、JavaScript や Node.js において、非同期処理のコールバック関数をエレガントに記述するための仕組みです。

英語の promise は、「制約」、「保障」などの意味を持ちます。IE11 ではサポートされていませんので es6-promise といったPolyfillが必要です。

 

コールバック地獄とは?

JavaScript では、ブロックする(処理が終わるまで待ち合わせる)関数よりも、非同期関数※の方が多様されます。ここで、例えば、膨大な演算(実は単に元の数を2倍するだけ)を行う非同期関数 aFunc1() があるとします。下記は、100の2倍を求める非同期関数の使用例です。

処理の完了を待たず、処理が完了した時点でコールバック関数が呼び出される。

JavaScript

// 引数を2倍にする非同期関数
function aFunc1(data, callback) {
    setTimeout(function() {
        callback(data * 2);
    }, Math.random() * 1000);
}

JavaScript

function sample_callback() {
    // 非同期関数を用いて100の2倍を求める
    aFunc1(100, function(value) {
        console.log(value);      // => 200
    });
}

 

単純に非同期関数を1回だけ呼び出すのであれば、上記で問題ありませんが、1回目で得られた値を用いて、aFunc1() を2度、3度呼び出そうとすると、下記の様な実装になります。

JavaScript

function sample_callback_hell() {
    aFunc1(100, function(data) {
        console.log(data);                  // => 200
        aFunc1(data, function(data) {
            console.log(data);              // => 400
            aFunc1(data, function(data) {
                console.log(data);          // => 800
            });
        });
    });
}

呼び出す回数に比例してコールバックのネストが深くなります。これを、「コールバック地獄」と呼びます。

 

タイミング問題

非同期関数はまた、処理の順序を制御できないという問題も含みます。下記の例では、100の2倍、200の2倍、400の2倍を求めようとしたにも関わらず、処理結果は 200, 400, 800 だったり、800, 200, 400 など、結果処理が順不同となるという問題があります。

JavaScript

function sample_timing_problem() {
    aFunc1(100, function(data) {
        console.log(data);      // => 200
    });
    aFunc1(200, function(data) {
        console.log(data);      // => 400
    });
    aFunc1(400, function(data) {
        console.log(data);      // => 800
    });
}

 

これは、jQueryにおける addClass や attr といったよく利用するメソッドも同じです。

$("#hoge").addClass("is-open") で is-open というclassを追加した直後に、$(".is-open") でDOMを取得しようとしても、タイミングによっては取得できない場合があります。

$(function(){
    $("#hoge").addClass("is-open");
    console.log($(".is-open")) // 取得できない
})

これは、jQuery内部で非同期関数として実装されているからです。

Promiseによる解決

これらの問題を解決するために考案されたのが Promise です。Promise は、約束、誓約、保証などの意味を持ちます。Promise は、待機(pending)、成功(fulfilled)、失敗(rejected)の3値を持つオブジェクトです。前述の非同期関数 aFunc1() を Promise を用いて書き直すと下記の様になります。処理を行う関数を引数とした Promise オブジェクトを返却します。

JavaScript

function aFunc2(data) {
    return new Promise(function(resolve) {
        setTimeout(function() {
            resolve(data * 2);
        }, Math.random() * 1000);
    });
}

 

Promise オブジェクトは then(resolve, reject) というメソッドを持ちます。then() は、Promise が成功または失敗になるまで処理を受け流し、成功時に resolve を、失敗時に reject をコールバック関数として呼び出します。

JavaScript

function sample_promise() {
    aFunc2(100).then(function(data) {
        console.log(data);      // => 200
    });
}

 

アロー関数を用いると、次のようにも記述できます。

JavaScript

function sample_promise2() {
    aFunc2(100).then((data) => {
        console.log(data);      // => 200
    });
}

 

さらに処理を継続するには、下記の様にします。

JavaScript

function sample_promise3() {
    aFunc2(100).then((data) => {
        console.log(data);      // => 200
        return aFunc2(data);
    })
    .then((data) => {
        console.log(data);      // => 400
        return aFunc2(data);
    })
    .then((data) => {
        console.log(data);      // => 800
    });
}

エラー処理

下記は、約 30% の確率でエラーとなる Promise 非同期関数です。

JavaScript

function aFunc3(data) {
    return new Promise(function(resolve, reject) {
        setTimeout(function() {
            if (Math.random() < 0.30) {
                reject(new Error('ERROR!'));
            } else {
                resolve(data * 2);
            }
        }, Math.random() * 1000);
    });
}

 

.then() は第一引数に成功時のコールバック関数、第二引数に失敗時のコールバック関数を指定します。エラーを考慮した呼び出し元は下記の様になります。

JavaScript

function sample_reject() {
    aFunc3(100).then(
        (data) => { console.log(data); },    // 成功時の処理
        (e) => { console.log(e); }           // 失敗時の処理
    );
}

 

上記は、下記の様に記述することもできます。.catch(reject) は、.then(undefined, reject) と同じ意味を持ちます。Promise は一度エラーが発生すると、最初に reject 関数が指定されるまで、then 処理をスキップします。

JavaScript

function sample_catch() {
    aFunc3(100).then((data) => {
        console.log(data);
        return aFunc3(data);
    })
    .then((data) => {
        console.log(data);
        return aFunc3(data);
    })
    .then((data) => {
        console.log(data);
    })
    .catch((e) => {
        console.log(e);
    });
}

throwを伴うエラー処理

.catch() はまた、処理中に発生した throw をキャッチすることもできます。下記の例では、aFunc3() 内部で発生したエラーや、2番目の処理で発生した例外を .catch() が受け止めます。

JavaScript

function sample_catch_with_throw() {
    aFunc3(100).then((data) => {
        console.log(data);
        return aFunc3(data);
    })
    .then((data) => {
        console.log(data);
        throw new Error('ERROR!!!');
    })
    .then((data) => {
        console.log(data);
    })
    .catch((e) => {
        console.log(e);
    });
}

Finally

.catch() の後ろに .then() を加えることで、成功時にも、失敗時にも常に実行される Finally のような処理を追加することができます。

JavaScript

function sample_finally() {
    aFunc3(100).then((data) => {
        console.log(data);
        return aFunc3(data);
    })
    .then((data) => {
        console.log(data);
        return aFunc3(data);
    })
    .then((data) => {
        console.log(data);
    })
    .catch((e) => {
        console.log(e);
    })
    .then(() => {
        console.log('*** Finally ***');
    });
}

ES2018(ES9) では、.finally() がサポートされました。

function sample_finally2() {
    aFunc3(100).then((data) => {
        console.log(data);
        return aFunc3(data);
    })
    .then((data) => {
        console.log(data);
        return aFunc3(data);
    })
    .then((data) => {
        console.log(data);
    })
    .catch((e) => {
        console.log("catch");
        console.log(e);
    })
    .finally(() => {
        console.log('*** Finally ***');
    });
}

すべてのタスクが完了したら(Promise.all())

Promise.all() は配列で指定されたすべての Promise タスクを待ち合わせ、すべてのタスクが完了した時点で .then() のコールバック関数を呼び出します。

 

JavaScript

function taskA() {
    return new Promise((callback) => {
        console.log("taskA start.");
        setTimeout(function() {
            console.log("taskA end.");
            callback();
        }, Math.random() * 3000);
    });
}
function taskB() {
    return new Promise((callback) => {
        console.log("taskB start.");
        setTimeout(function() {
            console.log("taskB end.");
            callback();
        }, Math.random() * 3000);
    });
}
function sample_all() {
    p1 = taskA();
    p2 = taskB();
    Promise.all([p1, p2]).then(() => {
        console.log("taskA and taskB are finished.");
    });
}

 

いずれかのタスクが完了したら(Promise.race())

Promise.race() は配列で指定された Promise タスクを待ち合わせ、いずれかひとつのタスクが完了した時点で、.then() のコールバック関数を呼び出します。

 

JavaScript

function sample_race() {
    p1 = taskA();
    p2 = taskB();
    Promise.race([p1, p2]).then(() => {
        console.log("taskA or task B is finished.");
    });
}

非同期関数を同期関数っぽく呼び出す(async/await)

ES2017 では、Promise に加え、async/await がサポートされました。こちらも、Internet Explorer を除く大半のモダンブラウザで利用可能です。async と await を用いることで、Promise に対応した非同期関数を、同期関数の様に呼び出すことが可能となります。同期関数の様に呼び出したい非同期関数を呼び出す際に await をつけます。await を呼び出す関数に async をつけます。

 

JavaScript

async function sample_async_await() {
    var val = 100;
    val = await aFunc2(val);
    console.log(val);                    // 200
    val = await aFunc2(val);
    console.log(val);                    // 400
    val = await aFunc2(val);
    console.log(val);                    // 800
}

エラー処理に対応するコードは下記の様になります。

 

JavaScript

async function sample_async_await_with_catch() {
    var val = 100;
    try {
        val = await aFunc3(val);
        console.log(val);
        val = await aFunc3(val);
        console.log(val);
        val = await aFunc3(val);
        console.log(val);
    } catch (e) {
        console.log(e);
    }
}

【PR】Udemyならスマホでもタブレットでも、いつでも気軽に学べます。

Udemyを実際に体験した方の感想記事もぜひご覧ください♪

関連コンテンツ

残り5時間!Udemyで新規受講生向けのセール実施中!新規ユーザー向けに対象コースが¥1,320から
エンジニア
オンライン学習
オンライン学習キャンペーン情報
デザイナー
デザイン
プログラミング
制作
公開日: 2019年11月07日
最終更新日: 2019年11月07日
残り5時間!Udemyで新規受講生向けのセール実施中!新規ユーザー向けに対象コースが¥1,320から
  • # Python
  • # Udemy
  • # セール情報
  • # タイムセール
オンスタ運営
【第1回】ノンプログラマー/プログラム初心者も楽しめる!ゴリラにも伝わるprocessing入門
エンジニア
プログラミング
公開日: 2016年12月06日
最終更新日: 2019年05月28日
【第1回】ノンプログラマー/プログラム初心者も楽しめる!ゴリラにも伝わるprocessing入門
オンスタ運営
CSVを使ってWordPressの投稿、カスタム投稿へ記事データを流し込む方法(Really Simple CSV Importer)
WordPress
エンジニア
プログラミング
制作
公開日: 2019年10月31日
最終更新日: 2019年10月31日
CSVを使ってWordPressの投稿、カスタム投稿へ記事データを流し込む方法(Really Simple CSV Importer)
  • # Really Simple CSV Importer
  • # WordPress
オンスタ運営
【Code.org】4歳から18歳まで!ビジュアルプログラミング言語でプログラミングが学べる『Code.org』について調べてみた
エンジニア
オンライン学習
プログラミング
制作
公開日: 2020年07月21日
最終更新日: 2020年07月21日
【Code.org】4歳から18歳まで!ビジュアルプログラミング言語でプログラミングが学べる『Code.org』について調べてみた
  • # Code.org
オンスタ運営
[JavaScript_中級者への道]  プロトタイプについて – [2] クラスを定義して呼び出す
プログラミング
公開日: 2020年02月18日
最終更新日: 2020年02月19日
[JavaScript_中級者への道] プロトタイプについて – [2] クラスを定義して呼び出す
  • # ES6
  • # JavaScript
  • # クラス
  • # プロトタイプ
オンスタ運営
[JavaScript_中級者への道] プロトタイプについて – [3] 身近な機能を実装してみる
プログラミング
公開日: 2020年02月19日
最終更新日: 2020年02月19日
[JavaScript_中級者への道] プロトタイプについて – [3] 身近な機能を実装してみる
  • # ES6
  • # JavaScript
  • # プロトタイプ
オンスタ運営
【JSプラグイン】スクロール連動型アニメーション!jQuery不要のJSプラグイン「AOS」!
Webデザイン
プログラミング
公開日: 2017年07月17日
最終更新日: 2019年05月28日
【JSプラグイン】スクロール連動型アニメーション!jQuery不要のJSプラグイン「AOS」!
オンスタ運営
JS初心者必見!JavaScriptの勉強ができるWEBサービス紹介【タイプ別】
プログラミング
マークアップ
公開日: 2018年12月28日
最終更新日: 2019年05月28日
JS初心者必見!JavaScriptの勉強ができるWEBサービス紹介【タイプ別】
オンスタ運営
[JavaScript_中級者への道] プロトタイプについて – [1] 概要
プログラミング
公開日: 2020年02月18日
最終更新日: 2020年02月19日
[JavaScript_中級者への道] プロトタイプについて – [1] 概要
  • # ES6
  • # JavaScript
  • # プロトタイプ
オンスタ運営
オンライン学習サイト比較!Webデザイン・プログラミングを学ぶならココ!(2019年版)
Webデザイン
オンライン学習
プログラミング
マークアップ
公開日: 2019年06月20日
最終更新日: 2019年09月05日
オンライン学習サイト比較!Webデザイン・プログラミングを学ぶならココ!(2019年版)
  • # cocoda!
  • # pickup
  • # Schoo
  • # TECHACADEMY
  • # Udemy
  • # オンライン学習
オンスタ運営
全記事一覧

web制作者にオススメのオンライン学習サイト「Udemy」

世界最大級のコースがラインナップ!電子書籍サイト「Udemy」

世界最大級のコースがラインナップ!オンライン学習サイト「Udemy」

「Udemy」ではいつでもどんなトピックでも、専門家が教える何千ものコースを用意されており、無料で学べるコースも多数あります。もうちょっと勉強したいな。スキルアップしたいな。と思ったら是非チェックしてみてください。