サイトトップ

Director Flash 書籍 業務内容 プロフィール

HTML5テクニカルノート

ES6: Promiseオブジェクトを使う


ECMAScript 2015(ECMAScript 6)に備わったPromiseは非同期の処理を扱うためのオブジェクトです。処理が成功したか、失敗したかによるコールバックがそれぞれ定められます。Promise()コンストラクタでつくるオブジェクトは、多くの場合関数の戻り値として用いられます。Promiseオブジェクトの使い方は「Using promises」が詳しいものの、実際に試せるコードは少ないようです。本稿では、動きが確かめられる簡単なコードをもとに解説します。

01 簡単な非同期の処理にPromiseオブジェクトを使ってみる

つぎの関数(async())にsetTimeout()メソッドで時間待ちの処理を定めました。関数の引数に渡されたミリ秒ののちに、console.log()メソッドで文字列が出力されます。


function async(arg) {
	console.log(`start`);
	setTimeout(() =>
		console.log(`${Math.floor(arg / 1000)} second delay is up`)
	, arg);
}
async(1000);

これはPromiseオブジェクトを用いて、つぎのコード001のように書き替えられます。Promise()コンストラクタに渡すのは関数です。その関数は引数としてコールバックの関数(resolve)を受け取ります。非同期の処理を終えたとき、そのコールバックを呼び出すのです。関数が返すPromiseオブジェクトに対しては、Promise.then()メソッドが呼び出せます。引数に渡すのは、処理が成功したときのコールバック関数です。このようにして、関数のおもな処理から非同期の処理を切り離すことができます。

コード001■Promiseオブジェクトで簡単な非同期の処理を扱う


function async(arg) {
	return new Promise((resolve) => {
		console.log(`start`);
		setTimeout(() =>
			resolve(arg)
		, arg);
	});
}
async(1000)
.then(
	(arg) => console.log(`${Math.floor(arg / 1000)} second delay is up`)
);

02 Promise.then()メソッドをつないで呼び出す

Promise.then()メソッドは、Promiseオブジェクトを返します。したがって、その戻り値に対して、さらにPromise.then()メソッドが呼び出せるということです。前掲コード001の関数(async())をつぎのように書き替えてみましょう。ウィンドウの中をクリックしたとき、コールバック関数(resolve)が呼び出されます。


function async(arg) {
	return new Promise((resolve) => {
		console.log('start');
		/* setTimeout(() =>
		resolve(arg)
		, arg); */
		window.addEventListener('click',
			(event) => resolve(arg)
		);
	});
}

Promise.then()メソッドの引数に渡すコールバックは、以下のようにまたPromiseインスタンスを返すことにします。すると、続けて呼び出すPromise.then()メソッドからコールバック(resolve)が渡せるのです。コンソールには、まずつぎのように出力されます。つまり、Promise.then()メソッドそのものは同期的に続けて処理されます。

start
finished

ウィンドウの中をクリックすると、非同期に分けたコールバック関数が予め定めたタイミングで呼び出されます。書き替えたスクリプトは以下のコード002にまとめました。

#1: 2 seconds have passed
#2: 1 second delay is up

let count = 0;

// async(1000)
async(new Date().getTime())
.then(
	(arg) => {
		return new Promise((resolve) => {
			const seconds = 1000;
			const passed = Math.floor((new Date().getTime() - arg) / 1000);
			console.log(`#${++count}: ${passed} second${(passed > 1) ? 's have' : ' has'} passed`);
			setTimeout(() =>
				resolve(seconds)
			, seconds);
		});
	}
)
.then(
	(arg) => console.log(`#${++count}: ${Math.floor(arg / 1000)} second delay is up`)
)
.then(console.log('finished'));

コード002■Promise.then()メソッドで複数の非同期処理をつないで呼び出す


let count = 0;
function async(arg) {
	return new Promise((resolve) => {
		console.log('start');
		window.addEventListener('click',
			(event) => resolve(arg)
		);
	});
}
async(new Date().getTime())
.then(
	(arg) => {
		return new Promise((resolve) => {
			const seconds = 1000;
			const passed = Math.floor((new Date().getTime() - arg) / 1000);
			console.log(`#${++count}: ${passed} second${(passed > 1) ? 's have' : ' has'} passed`);
			setTimeout(() =>
				resolve(seconds)
			, seconds);
		});
	}
)
.then(
	(arg) => console.log(`#${++count}: ${Math.floor(arg / 1000)} second delay is up`)
)
.then(console.log('finished'));

03 XMLHttpRequestとPromiseオブジェクトでweb APIのデータを読み込む

つぎは、webで公開されているAPIをXMLHttpRequestオブジェクトで読み込んでみます。APIとして用いるのは、「Open Notify」の「How Many People Are In Space Right Now」です。NASAのデータにもとづいて、今宇宙でどれだけの人たちが働いているのかわかります。Promiseは使わずに、XMLHttpRequestオブジェクトでJSONデータを読み込むのが以下のコードです。XMLHttpRequestクラスの使い方について、詳しくは「XMLHttpRequest の利用」をお読みください。JavaScriptコンソールには、つぎのような配列のデータが示されます。

Array(6)
0:{name: "Sergey Ryazanskiy", craft: "ISS"}
1:{name: "Randy Bresnik", craft: "ISS"}
2:{name: "Paolo Nespoli", craft: "ISS"}
3:{name: "Alexander Misurkin", craft: "ISS"}
4:{name: "Mark Vande Hei", craft: "ISS"}
5:{name: "Joe Acaba", craft: "ISS"}
length:6


const url = 'http://api.open-notify.org/astros.json';
function getpacePeople(url) {
	const request = new XMLHttpRequest();
	request.addEventListener('load', (event) =>
		console.log(JSON.parse(request.response).people)
	);
	request.open('GET', url);
	request.send();
}
getpacePeople(url);

Promiseクラスを使うには、関数からコンストラクタでつくったインスタンスを返します。つぎのコード001のように処理する関数を引数として定め、コールバック(resolve)はPromise.then()メソッドで与えます。

コード003■XMLHttpRequestとPromiseオブジェクトでweb APIのデータを読み込む


const url = 'http://api.open-notify.org/astros.json';
function getSpacePeople(url) {
	return new Promise((resolve) => {
		const request = new XMLHttpRequest();
		request.addEventListener('load', (event) =>
			resolve(request.response)
		);
		request.open('GET', url);
		request.send();
	});
}
getSpacePeople(url)
.then(
	(data) => console.log(JSON.parse(data).people)
);

04 処理が失敗したときのコールバックを加える

Promise.then()メソッドには、つぎのように処理が失敗したときのコールバック関数を第2引数として与えられます。Promise()コンストラクタの引数に定める関数は、第2引数で受け取るコールバック(reject)に失敗の処理を加えればよいのです。


const url = 'http://api.open-notify.org/astros.json';
function getpacePeople(url) {
	// return new Promise((resolve) => {
	return new Promise((resolve, reject) => {
		const request = new XMLHttpRequest();

		request.addEventListener('error', (event) =>
			reject(request)
		);

	});
}
getpacePeople(url)
.then(
	(data) => console.log(JSON.parse(data).people),
	(request) => console.log(request.status)
);

web APIのデータを読み込む前掲コード003に、処理の成功だけでなく、失敗した場合のコールバックを加えたのが以下のコード004です。併せてサンプル001にjsdo.itのコードを掲げました。ひとつ、Promise.then()メソッドについて補います。成功あるいは失敗のコールバック関数には、引数はひとつしか与えられません。複数の値を渡したいときは、オブジェクトなどにまとめてください。


return new Promise((resolve, reject) => {

	request.addEventListener('error', (event) =>
		// reject(request)
		reject({request: request, event: event})  // 複数の値はひとつのオブジェクトなどにまとめて渡す
	);

}

コード004■Promiseオブジェクトに処理の成功と失敗のコールバックを与えた


const url = 'http://api.open-notify.org/astros.json';
function getpacePeople(url) {
	return new Promise((resolve, reject) => {
		const request = new XMLHttpRequest();
		request.addEventListener('load', (event) =>
			resolve(request.response)
		);
		request.addEventListener('error', (event) =>
			reject(request)
		);
		request.open('GET', url);
		request.send();	
	});
}
getpacePeople(url)
.then(
	(data) => console.log(JSON.parse(data).people),
	(request) => console.log(request.status)
);

サンプル001■ES6: Getting number of people in space with Promise object


作成者: 野中文雄
作成日: 2017年10月24日


Copyright © 2001-2017 Fumio Nonaka.  All rights reserved.