2024-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>
※本記事内容の無断転載を禁じます。
ご連絡は以下アドレスまでお願いします★
Wav2Lipのオープンソース版を改造して外部から呼べるAPI化する
Wav2Lipのオープンソース版で静止画の口元のみを動かして喋らせる
【iOS】アプリアイコン・ロゴ画像の作成・設定方法
オープンソースリップシンクエンジンSadTalkerをAPI化してアプリから呼ぶ【2】
オープンソースリップシンクエンジンSadTalkerをAPI化してアプリから呼ぶ【1】
【Xcode】iPhone is not available because it is unpairedの対処法
【Let's Encrypt】Failed authorization procedure 503の対処法
【Debian】古いバージョンでapt updateしたら404 not foundでエラーになる場合
ファイアウォール内部のWindows11 PCにmacOS Sequoiaからリモートデスクトップする
進研ゼミチャレンジタッチをAndroid端末化する
Windows11+WSL2でUbuntuを使う【2】ブリッジ接続+固定IPの設定
Windows11のコマンドプロンプトでテキストをコピーする
【Apache】サーバーに同時接続可能なクライアント数を調整する
VirtualBoxの仮想マシンをWindows起動時に自動起動し終了時に自動サスペンドする
WindowsにSubversionをインストールしてGit Bashから使う
size_tとssize_tを使い分けてSegmentation Faultを予防する
apt upgradeしたあとnvidia-smiがダメになった場合
【Linux】iconv/libiconvをソースコードからインストール