JavaScript入門 非同期処理 Promise解説

最終更新日

ES6で追加されたPromiseは、非同期処理を扱うためのオブジェクトです。Promiseを使うことで非同期処理のネストが深くなることを避けられるため、コードの可読性が向上します。

Promiseの記法

構文:Promiseの記法

let prom = new Promise( 非同期処理を扱う関数 );                  ※1
let thenProm = prom.then( 非同期処理の成功時に実行する関数 );        ※2
let catchProm = thenProm .catch( 非同期処理の失敗時に実行する関数);     ※3
let finallyProm = new catchProm .finally( 非同期処理後に必ず実行する関数); ※4
  • prom:Promiseインスタンス
  • thenProm:thenメソッドのコールバック関数の処理が登録されたPromiseのインスタンス
  • catchProm:catchメソッドのコールバック関数の処理が登録されたPromiseのインスタンス
  • finallyProm:finallyメソッドのコールバック関数の処理が登録されたPromiseのインスタンス

Promiseによる非同期処理のハンドリングは、上記のように記述します。※1の非同期処理を扱う関数では、引数にresolve、rejectを使うことができます。resolve()が実行された場合には、※2のthenのコールバック関数が呼び出され、reject()が実行された場合、※3のコールバック関数が実行されます。そして、※4は、※2または※3のコールバック関数が終了すると、必ず呼び出されます。

Promiseのインスタンス化(※1)

Promiseで非同期処理のハンドリングを行うには、まずnew Promiseと記述してインスタンス化します。そして、このときの引数に渡す関数(asyncHandler)内に非同期処理を記述します。

const instance = new Promise( asyncHandler );  //new Promiseのコールバック関数内で非同期処理を行う
function asyncHandler( resolve, reject ){      //resolve、rejectの2つの引数が渡されてくる
    setTimeout()( () => {
        if( 非同期処理は成功か? ){              //非同期処理の結果に応じて、resolve、rejectを呼び分ける
            resolve( data );                   //非同期勝利が成功したとき、※2 thenの処理へ
        } else {
            reject( error );                   //非同期処理が失敗したとき、※3 catchの処理へ
        }
    });
}

このコールバック関数(asyncHandler)の2つの引数resolve、reject(変数名は任意で設定可)には、JavaScriptエンジンによって特別な関数が渡されます。これらの引数は、非同期処理の完了時点の状態によって、次のように使い分けます。

非同期処理が成功した時

非同期処理が成功したしたときは、resolveを実行します。resolveが呼び出されると、Promiseインスタンスのthenメソッドで登録したコールバック関数(※2)が実行されます。

非同期処理が失敗したとき

非同期処理が失敗したときには、rejectを実行します。rejectが呼び出されると、Promiseインスタンスのcatchメソッドで登録したコールバック関数(※3)が実行されます。

そのため、thenメソッドやcatchメソッドのコールバックには、非同期処理が完了した後の処理を記述することになります。

thenメソッド(※2)

thenメソッドに渡すコールバック関数には、非同期処理が成功した後に実行したい処理を記述します。

構文:thenメソッド

let thenProm = prom.then( successHandler );
function successHandler( data ){
    非同期処理が成功した後に実行したい処理
}
  • thenProm:successHanlerの処理が登録されたPromiseのインスタンス
  • prom :Promiseのインスタンス

thenメソッドのコールバック関数(successHanler)は、※1でresolveが呼び出されると実行されます。また、※1のresolve(data )の実行時に渡した値( data )は、コールバック関数successHanlerの引数( data )として渡されます。thenメソッドが実行されると、戻り値としてさらにPromiseのインスタンス( thenProm )が返ります。これに対してさらにthenメソッドをつなげていくことに、複数の非同期処理を順番に実行することもできます。

catchメソッド(※3)

catchメソッドに渡すコールバック関数には、非同期処理が失敗した後に実行したい処理を記述します。

構文:catchメソッド

let catchProm = prom.catch( errorHandler );
function errorHandler( error ){
    非同期処理が失敗した後に実行したい処理
}
  • catchprom:errorHanlerの処理が登録されたPromiseのインスタンス
  • prom:Promiseのインスタンス

catchメソッドのコールバック関数( errorHanler )は、※1でrejectが呼び出されたタイミングで実行されます。また、※1のreject( error )の実行時に渡した値( error )は、コールバック関数の引数( data )として渡されてきます。

thenメソッドの第2引数を設定した場合

thenメソッドの第2引数に関数を設定した場合は、非同期処理が失敗したとき(rejectが実行されたとき)にはcatchメソッドのコールバック関数と同じ意味になります。

構文:thenメソッドの第2引数を設定した場合

let instance = new Promise(...).then( 成功したとき,失敗したとき )

finallyメソッド(※4)

finallyメソッドに記述するのは、非同期処理が完了した後に必ず行いたい処理です。すなわち、finallyメソッドは、thenメソッドまたはcatchメソッドのコールバック関数の処理が完了した後に必ず実行されます。

構文:finallyメソッド

let finallyProm = prom.finally( finalHandler );
function finalHandler() {
    非同期処理が完了した後に必ず行いたい処理
}
  • finallyProm:finalHandlerの処理が登録されたPromiseのインスタンス
  • prom :Promiseのインスタンス

たとえば、サーバーへの通信中に画面上にローダーを表示していた場合など、成功・失敗に関わらずローダーの表示を取りやめたい時には、finallyメソッドにその処理を記述します。

Promiseによる非同期処理の例

非同期処理内でランダムな値を生成し、その値によって後続の処理(thenまたはcatch)が実行する例です

 let instance = new Promise( ( resolve,reject ) => {
    //1秒後に実行
    setTimeout( () => {
        //0~10のランダムな値を取得
        const rand = Math.floor( Math.random() * 11 );
        if( rand < 5 ){
            reject( rand );   //5未満のとき、エラーとする
        } else {
            resolve( rand );  //それ以外のとき、成功とする
        }
     }, 1000);
});
instance = instance.then( value => {
    console.log( `5以上の値[${ value }]が渡ってきました。` );
});
instance = instance.catch( errorValue => {
    console.log( `5未満の値[${ errorValue }]が渡ってきたためエラー表示` );
});
instance = instance.finally( () => {
    console.log( "処理を終了します。" );
});
実行結果
実行結果
実行結果
実行結果

このように記述した場合も、コールバック関数を使った非同期処理のハンドリングと同様、非同期処理を行った後に特定の処理(Promiseの場合はthenメソッドのコールバック関数)を実行できます。

チェーンメソッド

Promiseは、一般的にチェーンメソッドを使って記述します。チェーンメソッドとは、メソッドをつなげて記述する記法です。

構文:Promiseの記法

■メソッドを1つずつ実行した場合
let instance = new Promise(・・・);
instance = instance.then( ・・・ );
instance = instance.catch( ・・・ );
instance = instance.finally( ・・・ );

■チェーンメソッドでつなげて記述した場合
let instance = new Promise( ・・・ ).then( ・・・ ).catch( ・・・ ).finally( ・・・・ );

■メソッドごとに改行してOK
let instance = new Promise( ・・・ )
    .then( ・・・ )
    .catch( ・・・ )
    .finally( ・・・ );

なぜこのようなことができるかというと、thenメソッド、catchメソッド、finallyメソッドがPromiseインスタンスを返すためです。

チェーンメソッド

Promiseチェーン

複数のPromiseを直列で実行する方法を解説します。ここでの直列とは、前の非同期処理の完了を待って、次の非同期処理を実行するという意味です。このように、複数のPromiseによって非同期処理を順番に実行していくことをPromiseチェーンと呼びます。

構文:Promiseチェーンの記述方法

const Promiseインスタンス1 = new Promise( … );
const Promiseインスタンス2 = new Promise( … );
const Promiseインスタンス3 = new Promise( … );

Promiseインスタンス1
     .then( data1 => { return Promiseインスタンス2; } )
     .then( data2 => { return Promiseインスタンス2; } )
     .catch( error => { エラー発生時の処理 } )
     .finally( () => { 終了時の処理 } )

非同期処理を直接で実行するときには、thenメソッドのコールバック関数の戻り値(returnの後)にPromiseのインスタンスを設定します。これによって、Promiseインスタンス2、Promiseインスタンス3の中でresolveが呼び出されたときに、次のthenメソッドに処理が移ります。

Promiseチェーンのイメージ

また、Promiseチェーン内のいずれかのPromiseインスタンスでrejectが呼び出された場合には、catchメソッドに処理が移行します。

Promiseチェーン記述例

function promiseFactory( count ){                //※1
    return new Promise( ( resolve, reject ) => { 
        setTimeout( () => {
             count++;               //渡されてきたcountの値を1インクリメントする
             console.log( `$ { count }回目のコールです。時刻:[${ new Date().toTimeString() }]` );
             if( count === 3 ){                  //3回目のコールでエラー
                 reject( count );
             } else {
                 resolve( count );        //次のthenのコールバック関数の引数にcountが渡る
             }
        }, 1000 );
    });
}

promiseFactory( 0 )
 .then( count => { return promiseFactory( count ); } )//※2
 .then( count => { return promiseFactory( count ); } )//※3
 .then( count => { return promiseFactory( count ); } )//これらのthenのコールバック関数は実行されない
 .then( count => { return promiseFactory( count ); } )
 .catch( errorCount => { 
    console.log( `エラーに飛びました。現在のカウントは ${ errorCount }です。` );
  } ).finally( () => {
    console.log( `処理を終了します。` );
  } );
実行結果
実行結果

※1 Promiseインスタンスを返す関数の定義

上記のコードでは、Promiseのインスタンスを返すpromiseFactoryという関数を作成しています。そのため、promiseFactoryを実行すると、新しいPromiseのインスタンスが戻り値として取得されます。

※2 thenの戻り値にPromiseインスタンスを設定

thenメソッドのコールバック関数のreturnに続けてpromiseFactoryを実行しているため、promiseFactoryの実行結果としてPromiseインスタンスが取得されます。

※3 3回目でcatchに移行

3回目の実行でcountが3となるため、reject( count )が実行され、catchメソッドに移行します。そのため、これ以降のthenメソッドのコールバック関数は実行されません。


returnに続けて関数の実行を行った場合は、関数の実行結果(戻り値)がreturnによって返されます。
function hello(){
    return "こんにちは。";
}
function fn1(){
    return hello();   //末尾()が付いているため、hello変数の実行結果が実行元に返される
}
function fn2(){
    return hello();    //末尾に()が付いてないため、hello関数自体が実行元に返される
                       //(hello関数は実行されていない)
}
console.log( fn1() );
>こんにちは。
console.log( fn2() );
>function hello() { ... };

注意点があります。Promiseコンストラクタに渡す( resolve, reject ) => { ・・・ }のコールバック関数は、new Promise( インスタンス化 )を実行した時に実行します。そのため、次のように生成したPromiseインスタンスを使い回すことはできません。

非同期処理が一度しか実行されないケース

let count = 0;
function promiseFactory() {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            count++;
            console.log( `${count}回目のコールです。時刻:[${new Date().toTimeString()}]` );
            if(count === 3){    //3回目のコールでエラー
                reject(count);
            } else {
                resolve(count);
            }
        }, 1000 );
    });
}
const instance = promiseFactory();   //※1 この時点でpromiseFactory内のsetTimeoutが実行される
instance
 .then( () => { return instance; } )//setTimeoutの実行が再度行われるわけではないため、ログは出力されない
 .then( () => { return instance; } )//上行と同じ
 .then( () => { return instance; } )//上行と同じ
 .catch( errorCount => {
    console.log( `エラーに飛びました。現在のカウントは${ errorCount }です。` );
 } ).finally( () => {
    console.log( "処理を終了します。" );
 } );
実行結果
実行結果

※1の時点でpromiseFactory()の記述によって、promiseFactory関数内部のコードが実行され、Promiseのコールバック関数( resolve, reject ) => { … }が同期的に実行されます。それ以降、promiseFactory関数を再度実行されない限り( new Promiseを行わない限り )、このコールバック関数が呼び出されることはありません。そのため、1回目のコールのみログに出力されます。

Promiseの状態管理

Promiseのインスタンスは、内部で現在の状態(ステータス)を管理し、これによってPromiseのメソッド(catchやthen)などの呼び出しを制御しています。Promiseの静的メソッドも、これらの状態管理をもとに制御しています。

Promiseのステータス一覧

ステータス説明
pendingresolve、rejectが呼び出される前の状態
fulfilledresolveが呼び出された状態
rejectedrejectが呼び出された状態

「resolveが呼び出されるとthenに処理が移る」と説明しましたが、これはPromiseインスタンスの内部で保持しているステータスがfulfilledの状態に移行したことを表します。一方、rejectが実行されたときには、ステータスがrejectedとなります。fulfilledまたはrejectedに移行する前の状態をpendingと言います。また、fulfilledかrejectedのいずれかに移行した状態は、settledと言います。

Promiseインスタンスのステータスを確認

let promResolve, promReject;
const  prom = new Promise( (resolve, reject ) => {
    promResolve = resolve;      //※1 Resolveへの参照をpromResolveに保持
    promReject = reject;        //rejectへの参照をpromRejectに保持
});
console.log( prom );
> Promise {<pending>}           //resolve実行前はpendingの状態

promReject( "引数" );           //※2 resolveを実行
// promReject();                //※3

console.log( prom );
> Promise {<fulfilled>: "引数"} //fulfilledになる

上記のコードでは、※1でresolveへの参照をpromResolveに保持し、※2で実行しています。これによって、Promiseインスタンスが保持するステータスがpendingからfulfilledに移行していることが確認できます。また、Promiseのステータスは一度fulfilledまたはrejectedに移行すると変更できないため、仮に※3のpromReject()を※2の後に実行したとしても、Promiseのステータスはfulfilledのまま変わることはありません。

Promiseを使った並列処理

Promiseによる非同期処理を並列に実行する場合はPromiseの静的メソッドを使用します。

Promise.all

すべての非同期処理を並列に実行し、すべての完了を待ってから次の処理を行いたい場合には、Promise.allを使います。

構文:Promise.allの記法

Promise.all( iterablePromises )
    .then( ( resolvedArray ) => { … } )
    .catch( (error) => { … } );
  • iterablePromises:Promiseインスタンスを含む反復可能オブジェクト(ArrayやSet)を設定します。
  • resolvedArray:iterablePromisesに格納された各Promiseのresolveの実引数が格納された配列となって渡されてきます。なお、この配列の要素の順番はiterablePromiseに格納されているPromiseの順番に一致します。
  • error:最初にrejectedになったインスタンスのrejectの引数の値が渡ってきます。
  • 戻り値:Promiseのインスタンスが返されます。

Promise.allは、引数で与えられた反復可能オブジェクトに格納されている、すべてのPromiseインスタンスの状態がfulfilledになったときに、Promise.allに続くthenに処理を移行します。また、いずれかのPromiseインスタンスのステータスがrejectedになったときには、catchメソッドに処理が移ります。

Promise.allの実行イメージ

Promise.all( [ fulfilled, fulfilled, fulfilled ] )  →thenメソッドを実行
Promise.all( [ fulfilled, fulfilled, rejected] )  →catchメソッドを実行

Promise.allの記述例

function wait( ms ) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log( `${ms}msの処理が完了しました。` );
            resolve( ms );
        }, ms );
    });
}
const wait400 = wait(400);
const wait500 = wait(500);
const wait600 = wait(600);

Promise.all( [ wait400, wait500, wait600] )
    .then( ( [ wait400, wait500, wait600] ) => {
        console.log( "すべてのPromiseが完了しました。" );
        console.log( wait500, wait600, wait400 );
    } );

このように、Promise.allメソッドに渡した配列内のすべてのPromiseインスタンスがfulfilledになると、Promise.allに続くthenメソッドに処理が移ります。その際、thenのコールバック関数の引数に渡されるのは、各resolveに設定した実引数が格納された配列です。また、この処理はあくまで並列で行っているため、promiseチェーンを使った直列処理とはことなります。

直列の場合には前の処理の完了を待ってから次の処理が実行されますが、並列の場合にはすべての処理が一斉に実行されます。

Promise.race

Promise.raceは、複数のPromiseインスタンスのいずれかの状態がsettled(fulfilledまたはrejected)になったときに、Promise.raceに続くthenメソッドまたはcatchメソッドを実行します。

Promise.raceの実行イメージ

Promise.race( [ pending, pending, fulfilled ] )  →thenメソッドを実行
Promise.race( [ pending, rejected, pending] )    →catchメソッドを実行

構文:Promise.raceの記法

Promise.race( iterablePromises )
    .then( ( firstResolvedValue ) => { … } )
    .catch( ( error ) => { … } );
  • iterablePromises:Promiseインスタンスを含む反復可能オブジェクト(ArrayやSet)を設定します。
  • firstResolvedValue:最初にfulfilledになったインスタンスのresolveの引数の値が渡されてきます。
  • error:最初にrejectedになったインスタンスのrejectの引数の値が渡されてきます。
  • 戻り値:Promise.allSettledのインスタンスが返されます。

Promise.raceの記述例

//resolve()を100ミリ秒後に実行するPromiseインスタンス
const myResolve = new Promise( resolve => {
    setTimeout( () => {
        resolve( "resolveが呼ばれました。" );
        console.log("myResolveの実行が終了しました。");
    }, 100);    
} );

//reject()を200ミリ秒後に実行するPromiseインスタンス
const myReject = new Promise( ( _, reject ) => {   //resolveを使わないため_としておく
    setTimeout( () => {
        reject( "rejectが呼ばれました。" );
        console.log("myRejectの実行が終了しました。");
    }, 100);
} );

Promise.race( [ myReject, myResolve ] )
    .then( value => {
        console.log( value );
    } ).catch( value => {
        console.log( value );
    });
>myResolveの実行が終了しました。    //※1
>resolveが呼ばれました。             //※2
>myRejectの実行が終了しました。        //※3

※1 myResolveのsetTimeoutに設定したコールバック関数が100ミリ秒後に実行されます。このとき、myResolveに格納されるPromiseインスタンスのステータスがfulfilledになります。

※2 いずれかのインスタンスがfulfilled(またはrejected)になった時点で後続の処理が呼び出されるため、thenメソッドが実行されます。このとき渡される引数(Value)に※1のresolveの実引数の値となります。

※3 myRejectのsetTimeoutに設定したコールバック関数が200ミリ秒後に実行されます。myRejectの内部ではrejectが呼び出されますが、これによる後続の処理(catchメソッドのコールバック関数)の実行は発生しません。

myResolveやmyRejectの待機時間を逆にしてやると、myRejectが先にsettledの状態に遷移するため、catchメソッドが実行されることになります。

Promise.any

Promise.anyは、複数のPromiseインスタンスのいずれかがfulfilledになったタイミングでthenメソッドに処理を移します。また、すべてのインスタンスの状態がrejectedになったときに、catchメソッドを実行します。

Promise.anyの実行イメージ

Promise.any( [ rejected, pending, fulfilled ] )  →thenメソッドを実行
Promise.any( [ rejected, rejected, rejected] )    →catchメソッドを実行

Promise.raceと似ていますが、Promise.raceの場合にはPromiseインスタンスのいずれかがsettledになった時点でthenまたはcatchに処理を移します。

構文:Promise.anyの記法

Promise.any( iterablePromises )
    .then( ( resolvedValue) => { … } )
    .catch( ( error ) => { … } );
  • iterablePromises:Promiseインスタンスを含む反復可能オブジェクト(ArrayやSet)を設定します。
  • resolvedValue:最初にfulfilledになったインスタンスのresolveの引数の値が渡れてきます。
  • error:AggregateErrorという特殊なオブジェクト
  • 戻り値:Promiseインスタンスが返されます。

Promise.anyの記述例

//resolve()を200ミリ秒後に実行するPromiseインスタンス
const myResolve = new Promise( resolve => {
    setTimeout( () => {
        resolve( "resolveが呼ばれました。" );
        console.log("myResolveの実行が終了しました。");
    }, 200);    
} );

//reject()を100ミリ秒後に実行するPromiseインスタンス(rejectがmyResolveのresolveより前に呼び出される)
const myReject = new Promise( ( _, reject ) => {   //resolveを使わないため_としておく
    setTimeout( () => {
        reject( "rejectが呼ばれました。" );
        console.log("myRejectの実行が終了しました。");
    }, 100);
} );

Promise.any( [ myReject, myResolve ] )
    .then( value => {
        console.log( value );
    } ).catch( error => {
        console.log( error);
    });
>myResolveの実行が終了しました。    //※1
>resolveが呼ばれました。           //※2
>myRejectの実行が終了しました。    //※3

※1 myRejectが100秒後に実行されます。このインスタンスの結果はrejectedのため、Promise.anyは他のインスタンスの実行結果を待ちます。

※2 myResolveが200ミリ秒後に実行されています。myResolveのインスタンスのステータスはfulfilledとなるため、後続のthenの処理に移行します。

※3 thenのコールバック関数の実行時に渡される引数(value)は、※2のresolveの実引数の値となります。

このように、Promise.anyでは、いずれかのインスタンスがfulfilledになるのを待ちます。また、すべてのPromiseインスタンスがrejectedとなったときにはcatchメソッドに処理が移りますが、このとき渡される引数はAggregateErrorという特殊なErrorオブジェクトになります。

Promise.allSettled

Promise.allSettledは、すべてのPromiseインスタンス状態がsettled(fulfilledまたはrejected)になったときにthenメソッドに処理を移行します。

Promise.allSettledの実行イメージ

Promise.allSettled( [ fulfilled , rejected, fulfilled ] )  →thenメソッドを実行

allSettledの場合はcatchメソッドは使いません。thenメソッドのコールバック関数に、それぞれのPromiseインスタンスの状態(status)の値(valueまたはreason)が対で格納された配列が渡されます。

構文:Promise.allSettledの記法

Promise.allSettled( iterablePromises )
    .then( ( arry ) => { … } )

iterablePromises:Promiseインスタンスを含む反復可能オブジェクト(ArrayやSet)を設定します。

arry :Promiseインスタンスの状態と値が対になって格納された配列が渡されます。

例:

[
    { status: "fulfilled", value: "resolveの値" },  //fulfilledの場合
    { status: "fulfilled", reason: "rejectの値" }   //rejectの場合
]

戻り値 :Promiseインスタンスが返されます。

Promise.allSettledの記述例

//resolve()を200ミリ秒後に実行するPromiseインスタンス
const myResolve = new Promise( resolve => {
    setTimeout( () => {
        resolve( "resolveが呼ばれました。" );
        console.log("myResolveの実行が終了しました。");
    }, 200);    
} );

//reject()を100ミリ秒後に実行するPromiseインスタンス
const myReject = new Promise( ( _, reject ) => {         //resolveを使わないため_としておく
    setTimeout( () => {
        reject( "rejectが呼ばれました。" );
        console.log("myRejectの実行が終了しました。");
    }, 100);
} );

Promise.allSettled( [ myReject, myResolve ] )
    .then( arry => {
        for( const { status, value, reason} of arry ){   //分割代入で各プロパティの値を抽出
            console.log(`ステータス:[${ status }], 値:[${ value}], エラー:[${ reason}]`);
        }
    });
>myRejectの実行が終了しました。                                            //※1
>myResolveの実行が終了しました。                                           //※1
>ステータス:[rejected], 値:[undefined], エラー:[rejectが呼ばれました。]     //※2
>ステータス:[fulfilled], 値:[resolveが呼ばれました。], エラー:[undefined]   //※3
  • ※1 myReject、myResolveの状態がsettledになるまで、後続の処理(then)を待機します。
  • ※2 rejectedの場合にはreasonプロパティに対してrejectの引数の値が渡されます。
  • ※3 fulfilledの場合にはvalueプロパティに対してresolveの引数の値が渡されます。

Promise.allSettledでエラーハンドリングしたい場合には、statusプロパティの値を確認して条件判定を加えるようにします。

その他の静的メソッド

Promise.resolve(引数)

Promise.resolveは、fulfilledの状態のPromiseインスタンスを返します。特定の処理を非同期処理として実行したい場合に使います。

Promise.resolveの使用例

let val = 0;
Promise.resolve().then( () => {
    console.log(`valの値は[${ val }]です。`);  //非同期処理のためグローバルコンテキスト終了後に実行される
})

val = 1;
console.log(`グローバルコンテキスト終了`);
>グローバルコンテキスト終了
>valの値は[1]です。

Promise.reject(引数)

Promise.rejectは、rejectedの状態のPromiseインスタンスを返します。基本的にはPromise.resolveと同じですが、エラーのみ非同期とする実装はほとんどないため、使用頻度は低い。

Promise.rejectの使用例

Promise.reject( "エラーの理由" ).catch ( error => {
    console.log( error );
});

val = 1;
console.log(`グローバルコンテキスト終了`);
>グローバルコンテキスト終了
>エラーの理由

ジョブキューとタスクキューの関係

Promiseによる非同期処理の実行は、タスクキューではなく、ジョブキュー(Job Queue)と呼ばれるキューによって管理されています。ジョブキューは、Promiseによる非同期処理の管理を行うための専用のキューです。また、Promiseによって追加される非同期処理はマイクロタスク(Microtask)とも呼びます。

  • コールスタックが空になったときに、ジョブキューにジョブが格納されている場合には、格納されているすべてのジョブを実行する。このとき、タスクキューにタスクが格納されている場合にもジョブの実行が優先される。
  • コールスタックが空の状態かつジョブキューが空の場合に、タスクキューに格納されたタスクが実行される。
タスクキューとジョブキューの関係
setTimeout( function task() { console.log( "Taskの実行" ) } );  //タスクキューに登録されたタスク
Promise.resolve().then( function job1() { console.log( "Job1の実行" ) } ); //ジョブはタスクキューの前に実行される
Promise.resolve().then( function job2() { console.log( "Job2の実行" ) } ); //ジョブキューに登録されたジョブがすべて処理されるまで、タスクキューのタスクは実行されない
console.log( "グローバルコンテキスト終了" );
実行結果
実行結果