HTML5テクニカルノート
ES6: Promiseオブジェクトを使う
- ID: FN1710003
- Technique: ECMAScript
- Edition: 2015
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:6const 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.