Promiseの基本~Promiseの挙動サンプル、コード例
Promiseとは?
Promiseとは、JavaScript(TypeScript)で、非同期処理を統一的に扱うインタフェースです。
このページでは、TypeScriptを前提にPromiseの使い方をまとめます。
Promiseオブジェクトの生成
1. 返り値がPromiseの関数呼び出し
const ret = fetch(`http://abc.com/`);Promiseオブジェクトは、呼び出した関数内で行われます。
2. Promise.resolve
const resolved = Promise.resolve(`resolve value`);Promiseの前にnewは不要です。
3. Promise.reject
const rejected = Promise.reject(new Error(`rejected`));これも、Promiseの前にnewは不要です。
4. 非同期処理をPromiseでラップ
setTimeout関数の例です。
正常終了する場合にはresolve、異常終了する場合にはrejectを呼び出すのが基本です。
また、new Promise<...>の部分は、型変数を指定しましょう。今回は、stringを指定しています。
逆に、コールバック関数内のresolve、rejectの型は自動で付きますので、型変数は不要です。
//正常終了時の返り値の型(今回の場合は「string」)をPromiseの型変数に指定
const promise1 = new Promise<string>((resolve, reject) => {
setTimeout(() => {
try {
//非同期処理内の処理はtry~catchで囲むのが基本
resolve('return value'); //処理の返り値はresolveに渡す
} catch (e) {
reject(e); //エラーはrejectに渡す
}
}, 1000);
});Promise作成時の実行順
下記ソースコードを実行して、実行順を確認してみましょう。
const OrderOfExecutionTestSub = () => {
const promise = new Promise<string>((resolve, reject) => {
console.log('2'); // Promise内部
setTimeout(() => {
console.log('5'); // 非同期処理本体 resolve('resolved');
}, 0);
}).then((value) => {
console.log(value);
console.log('6'); // then内部 });
console.log('3'); // Promise作成直後};
const OrderOfExecutionTestMain = () => {
console.log('1'); // プログラム開始 OrderOfExecutionTestSub();
console.log('4'); // プログラム終了};
OrderOfExecutionTestMain();
// 1
// 2
// 3
// 4
// 5
// resolved
// 6
つまり、次のような順序で実行されるということになります。
new Promiseで与えたコールバック関数内部の処理「2」は、その場で実行される(=常に「同期的」に実行する)- Promise内部の非同期処理(
setTimeout内部)は後回しにして、「3」→「4」と、いったんプログラムの末尾まで実行する - プログラム末尾までたどりついた後、Promise内部の非同期処理「5」を実行する
- メソッドチェーンでつながれた
thenやcatch「6」を実行する
Promise内部のエラーハンドリング
さきほどのnew Promiseで与えたコールバック関数内部の処理「2」で発生したエラーは、Promiseのcatch句で補足できます。
const errorCatchable = () => {
const promise = new Promise<string>((resolve, reject) => {
setTimeout(() => {
resolve('some value');
}, 0);
throw new Error('catchable'); }).catch((e) => {
console.log('catched');
});
};
errorCatchable();
// catched一方で、Promise内部の非同期処理(setTimeout内部)「5」で発生したエラーは補足できず、そのままエラーになります。
const errorNotCatchable = () => {
const promise = new Promise<string>((resolve, reject) => {
setTimeout(() => {
throw new Error('not catchable'); }, 0);
}).catch((e) => {
console.log('catched');
});
};
errorNotCatchable();
// Error: not catchable
// at Timeout._onTimeout (C:\nodejs\src\Promise.ts:X:X)
// at listOnTimeout (internal/timers.js:531:17)
// at processTimers (internal/timers.js:475:7)
// ...異常終了を表す場合には、冒頭で説明したようにrejectを使いましょう。
const errorCatchable2 = () => {
const promise = new Promise<string>((resolve, reject) => {
setTimeout(() => {
reject(new Error('catchable')); }, 0);
}).catch((e) => {
console.log('catched');
});
};
errorCatchable2();
// catchedあるいは、必要に応じて、全体をtry~catchで囲んでもいいでしょう。
その場合には、catch句でrejectを呼び出しましょう。
const errorCatchable3 = () => {
const promise = new Promise<string>((resolve, reject) => {
setTimeout(() => {
try { //各種処理
throw new Error('catchable');
} catch (e) { reject(e); // rejectを呼び出す } }, 0);
}).catch((e) => {
console.log('catched');
});
};
errorCatchable3();
// catchedthen、catch、finallyメソッド
Promiseオブジェクトを作った後の処理は、thenメソッド、catchメソッド、finallyメソッドで書いていきます。
基本的に、正常系の処理はthenメソッド内に書いていきます。
import fetch from 'node-fetch';
const fetchZipcode = () => {
const yuubinApi = fetch(
'https://zipcloud.ibsnet.co.jp/api/search?zipcode=7830060'
) //APIにアクセスしてresponseを取得
.then((res) => res.json()) //正常系。responseからjsonを取得
.then((json) => {
console.log(json); //正常系続き。jsonを表示する
})
.catch((err) => {
console.log(err); //エラーの場合の処理
});
};
fetchZipcode();
//{
// message: null,
// results: [
// {
// address1: '高知県',
// ...
// prefcode: '39',
// zipcode: '7830060'
// }
// ],
// status: 200
//}以下、Promiseの挙動を知るための例です。
1. resolveされた値は「then」メソッドで受け取る
const promiseTestResolved = () => {
const promise = Promise.resolve('resolved');
promise.then((value) => {
console.log(value);
});
};
promiseTestResolved();
//resolved2. rejectされた値は「catch」メソッドで受け取る
const promiseTestRejected = () => {
const promise = Promise.reject('rejected');
promise.catch((err) => {
console.log(err);
});
};
promiseTestRejected();
//rejected途中にthenメソッドを挟んでもかまいません。一番近いcatchメソッドで、rejectされた値を受け取れます。
const promiseTestRejected2 = () => {
const promise = Promise.reject('error');
promise
.then((value) => { // thenは無視される
console.log(`resolved`);
console.log(value);
})
.catch((err) => { // rejectされた値を受け取る console.log(`rejected`); console.log(err); });};
promiseTestRejected2();
//rejected
//error3. then、catchメソッドは常に新しいPromiseオブジェクトを返す
const isSameObject = () => {
const promise1 = Promise.resolve('resolved1');
const promise2 = promise1.then(() => {
return 'resolved2';
});
console.log(promise1 === promise2);};
isSameObject();
//false4. then、catchメソッドで普通の値をreturn→resolve
thenメソッドの中で、普通の値(=Promise.reject以外の値)をreturnで返すことで、新しく値をresolveすることができます。
そのresolveした値は、後に続くthenメソッドで、その値を使うことができます。
const resolveInThen = () => {
const promise = Promise.resolve('')
.then(() => {
return 100; // 普通の値でOK(Promise<number>が返り値になる) })
.then((value) => {
console.log(value); // 100
return Promise.resolve('abc'); // Promise.resolveを使ってもOK })
.then((value) => {
console.log(value); // 'abc' });
};
resolveInThen();
//100
//abccatchメソッドが間に入っていても無視されます。
const resolveInThen2 = () => {
const promise = Promise.resolve('')
.then(() => {
return 100;
})
// rejectされていないので無視される
.catch(() => { console.log(`ignored1`); }) .then((value) => {
console.log(value);
return Promise.resolve('abc');
})
// 直前でrejectされていないので無視される
.catch(() => { console.log(`ignored2`); }) .then((value) => {
console.log(value);
});
};
resolveInThen2();
//100
//abccatchメソッドの中からも、普通の値をreturnで返すことで、新しく値を「resolve」することができます。
catch内から単にreturnをしただけでは「reject」にはならないことに注意しましょう。
const resolveInCatch = () => {
const promise = Promise.reject('error')
// 直前でresolveされていないので無視される
.then((v) => {
console.log(v);
return 'ignored';
})
.catch((e) => {
console.log(e);
return 'return value of catch'; })
.then((value) => {
console.log(value); // return value of catch });
};
resolveInCatch();
//error
//return value of catch5. then、catchメソッド内で「return Promise.reject(...)」または「throw」→reject
thenメソッドのreturnでPromise.reject(...)を値をreturnすると、「reject」することができます。「reject」した結果は、一番近いcatchで受け取ることができます。
const rejectInThen = () => {
const promise = Promise.resolve('')
.then(() => {
return Promise.reject('error'); })
.catch((err) => {
console.log(err); // error });
};
rejectInThen();
//errorthenメソッドでthrow errorしても「reject」できます。
const rejectInThen2 = () => {
const promise = Promise.resolve('')
.then(() => {
throw 'error'; })
.catch((err) => {
console.log(err); // error });
};
rejectInThen2();catchメソッド内でrejectしたいときも、上の2つのどちらかの方法を取りましょう。
rejectした値は、その次のcatchメソッドで使えます。
const rejectInCatch = () => {
const promise = Promise.reject('')
.catch(() => {
return Promise.reject('error'); })
.catch((err) => {
console.log(err); // error });
};
rejectInCatch();6. finallyメソッドは必ず実行される
finallyメソッドは必ず実行されます。なお、finallyメソッドは、引数を取れません。
const finally1 = () => {
const promise = Promise.resolve('resolved')
.then((value) => {
console.log(value);
})
.catch((error) => {
console.log(error);
})
.finally(() => { console.log('finally'); });};
finally1();
//resolved
//finally7. finallyメソッド以降のthen、catchメソッドは実行されない
finallyメソッド以降の、thenメソッド、catchメソッドは実行されません。
const finally2 = () => {
const promise = Promise.resolve('resolved')
.then((value) => {
console.log(value);
return 'then1';
})
.catch((error) => {
console.log(error);
})
.finally(() => { console.log('finally'); }) .then((value) => {
// 絶対に実行されない
console.log(value);
});
};
finally2();
//resolved
//finally