2023-10-04
Node/javascriptは処理が非同期で行われるので、普通にやると結果を待たずに次の処理が実行されてしまう。
<やりたいこと>
・標準httpsモジュールで外部コンテンツを取得、「取得が終わってから」次の処理へ行きたい。
・タイムアウト値を設定してタイムアウトしたらhttps通信を中止したい。
・ステータスHTTP 200番台以外はエラーとしたい。
<解決方法>
・同期したい(待ちたい)処理をまるまるPromiseクラスで囲ってしまえばよい。
・必要なデータの取得が済んだらPromise内でresolve関数に渡せば呼んだ側の戻り値としてデータが返る。
・エラーが発生した場合Errorオブジェクトを生成してreject関数に渡せば呼んだ側のcatchブロックのerrとしてエラーが返る。
・そのようにしてPromiseから返ったデータの型によって処理を分けたい場合res.constructor.nameを見ればクラスがわかるので分岐可能。
・optionsにtimeoutをミリ秒で指定可能。タイムアウトイベントが発生するのでそのときabortを呼べばよい。
<ソースコード>
JavaScript | promise_https.js | GitHub Source |
// 同期的にhttps通信するサンプル const https = require('https'); function promiseRequest(url, options, postData) { return new Promise(function (resolve, reject) { var req = https.request(url, options, (res) => { console.log('statusCode:', res.statusCode); console.log('headers:', res.headers); //200台以外はエラーとする場合 if(res.statusCode >= 300) { reject(new Error('res.statusCode >= 300')); } var chunks = []; //チャンクレスポンスストック配列 res.on('data', chunk => chunks.push(Buffer.from(chunk))) .on('end', () => { //チャンクレスポンスストリーム終了 var buffer = Buffer.concat(chunks); resolve(buffer); }); }); req.on('error', (err) => { reject(err); }); req.on('timeout', () => { req.abort(); reject(new Error('request timed out')); }); if(postData != null) { req.write(postData); } req.end(); }); } async function main() { const url = 'https://www.yahoo.co.jp/'; var options = { method: 'GET', timeout: 5000, //headers: { //'Content-Type': 'application/json', //}, }; //var postData = JSON.stringify({ // 'limit' : 50 //}); var postData = null; try { const res = await promiseRequest(url, options, postData); //Promiseがresolveするとresがここに返る console.log(res.constructor.name); // =Buffer console.log(res.toString()); } catch(err) { //Promiseがrejectするとerrがここに返る console.log(err.constructor.name); // =Error console.log(err.stack); } } main();
・async宣言した関数内でawait promiseRequestとし、https通信完了まで待つ。try~catchで囲わないと正常に動作しないので注意。
・HTTP 200番台以外をエラーとし、自分でErrorオブジェクトを生成しrejectで抜けている。
・HTTPS通信のデータ取得はチャンク(chunk)という細切れで数回に分けてストリーム取得されるため、chunks配列に都度格納し、endイベント発生時に一括Buffer変換し、処理成功としてresolveで呼び出し元へデータを返す。
・通常HTMLなどのテキストデータなら、受け取った側はtoStringで文字列変換して表示できる。
・optionsをPOSTにしてContent-Typeをjson、postData JSONデータをセットすれば、POSTメソッドによるREST APIの呼び出しにも対応する。
・optionsにtimeout5000=5秒でreq.on(timetout)が呼ばれる。その時忘れずabortして処理を中止し自前でErrorオブジェクトを生成しrejectで抜ければOK。
<実行結果>
node promise_https.js statusCode: 200 headers: { server: 'ATS', date: 'Sat, 17 Sep 2022 01:53:50 GMT', 'content-type': 'text/html; charset=UTF-8', 'accept-ranges': 'none', 'cache-control': 'private, no-cache, no-store, must-revalidate', expires: '-1', pragma: 'no-cache', 'set-cookie': [ 'B=1medvfthiaa5e&b=3&s=th; expires=Tue, 17-Sep-2024 01:53:50 GMT; path=/; domain=.yahoo.co.jp', 'XB=1medvfthiaa5e&b=3&s=th; expires=Tue, 17-Sep-2024 01:53:50 GMT; path=/; domain=.yahoo.co.jp; secure; samesite=none' ], vary: 'Accept-Encoding', 'x-content-type-options': 'nosniff', 'x-frame-options': 'SAMEORIGIN', 'x-vcap-request-id': '07baffbf-08d0-4fa1-593d-38c6fbd75061', 'x-xss-protection': '1; mode=block', age: '0', 'transfer-encoding': 'chunked', connection: 'close' } Buffer <title>Yahoo! JAPAN</title> ....中略.... })();
<テストでtimeout: 1としてタイムアウトを発生させた場合>
node promise_https.js Error Error: request timed out at ClientRequest.<anonymous> (/home/hogeuser/promise_https.js:31:10) at ClientRequest.emit (node:events:394:28) at TLSSocket.emitRequestTimeout (node:_http_client:761:9) at Object.onceWrapper (node:events:513:28) at TLSSocket.emit (node:events:406:35) at TLSSocket.Socket._onTimeout (node:net:486:8) at listOnTimeout (node:internal/timers:557:17) at processTimers (node:internal/timers:500:7) </anonymous>
※本記事内容の無断転載を禁じます。
ご連絡は以下アドレスまでお願いします★
Windowsのデスクトップ画面をそのまま配信するための下準備
WindowsでGPUの状態を確認するには(ASUS系監視ソフトの自動起動を停止する)
CORESERVER v1プランからさくらインターネットスタンダートプランへ引っ越しメモ
さくらインターネットでPython MecabをCGIから使う
さくらインターネットのPHPでAnalytics-G4 APIを使う
インクルードパスの調べ方
【Git】特定ファイルを除外する.gitignore
【Ubuntu/Debian】NVIDIA関係のドライバを自動アップデートさせない
【Python】Spacyを使用して文章から出発地と目的地を抜き出す
【Windows10】リモートデスクトップ間のコピー&ペーストができなくなった場合の対処法
Windows11+WSL2でUbuntuを使う【2】ブリッジ接続+固定IPの設定
Windows版Google Driveが使用中と言われアンインストールできない場合
VirtualBoxの仮想マシンをWindows起動時に自動起動し終了時に自動サスペンドする
【Apache】サーバーに同時接続可能なクライアント数を調整する
【C/C++】小数点以下の切り捨て・切り上げ・四捨五入
Googleファミリーリンクで子供の端末の現在地がエラーで取得できない場合
【Linux】iconv/libiconvをソースコードからインストール
Ubuntu Server 21.10でイーサリアムブロックチェーン【その5】