CreateLife ~AlwaysLatest~

Promiseとは

JavaScript

前提

非同期処理はブラウザやnode.jsが提供する処理だが、PromiseはECMAScriptの標準ビルトインオブジェクトである。ので、非同期処理と密接な関係にあるPromiseだが、独立した機能である

Promiseの挙動

第一引数にcallback関数を入れる

このcallback関数はnewされた瞬間に実行される

let promise = new Promise(()=>{
	console.log('new promise!');
});

インスタンス化されたオブジェクトが持つプロパティ

  • PromiseState: Pending
  • PromiseResult: undefined

プロパティの挙動

  • callback関数の第一引数の関数オブジェクト(resolve)を実行

⇒PromiseState が fulfilledに ⇒引数がPromiseResultに入る

let promise = new Promise((resolve, reject)=>{
	resolve('hello');
});
console.log(promise); // [[PromiseState]]: "fulfilled",[[PromiseResult]]: "hello"
  • callback関数の第二引数の関数オブジェクト(reject)を実行

⇒PromiseState が rejectedに ⇒引数がPromiseResultに入る

let promise = new Promise((resolve, reject)=>{
	reject('error');
});
console.log(promise); // [[PromiseState]]: "rejected",[[PromiseResult]]: "error"

then,catch,finallyの挙動

PromiseのPromiseStateプロパティによって実行されるメソッドが変わる

PromiseState: Pending

  • then,catch,finallyに渡した関数を内部的に保管しておく(実行しない)
let promise = new Promise((resolve, reject)=>{});
promise.then(()=>{console.log('then')}); // 実行されない
promise.catch(()=>{console.log('catch')}); // 実行されない
promise.finally(()=>{console.log('finally')}); // 実行されない

PromiseState: fulfilled

  • thenとfinallyが実行される(内部的に保管されたものも実行)
  • thenは第一引数にPromiseResultを受け取れる
let promise = new Promise((resolve, reject)=>{
	resolve('hello');
});
promise.then((value)=>{
	console.log(value); // hello
});
promise.catch(()=>{}); //実行されない
promise.finally(()=>{
	console.log('finally'); // finally
});

PromiseState: rejected

  • catchとfinallyが実行される(内部的に保管されたものも実行)
  • catchは第一引数にPromiseResultを受け取れる
let promise = new Promise((resolve, reject)=>{
	reject(new Error('error'));
});
promise.then(()=>{}); // 実行されない
promise.catch((error)=>{
	console.log(error.message); // error
});
promise.finally(()=>{
	console.log('finally'); // finally
});

Promiseチェーンとは

定義

then,catch,finallyはチェーン状に繋いで記述することができること

特徴8つ

1. thenの中でreturnされたらプロミスチェーン上の次のthenが実行され、次のthenの第一引数にreturnされた値が入る

new Promise((resolve)=>resolve(1))
.then((val)=>{
	console.log(val); // 1
	return 2;
})
.then((val)=>{
	console.log(val); // 2
})

2. thenの中でthrowされたらプロミスチェーン上の次のcatchが実行され、次のcatchの第一引数にthrowされた値が入る

new Promise((resolve)=>resolve(1))
.then((val)=>{
	console.log(val); // 1
	return 2;
})
.then((val)=>{
	console.log(val); // 2
	throw new Error(3);
})
.catch((error)=>{
	console.log(error.message); // 3
})

3. catchの中でもthrowと同様の動きをする

new Promise((resolve)=>resolve(1))
.then((val)=>{
	console.log(val); // 1
	return 2;
})
.then((val)=>{
	console.log(val); // 2
	throw new Error(3);
})
.catch((error)=>{
	console.log(error.message); // 3
	throw new Error(4);
})
.catch((error)=>{
	console.log(error.message); // 4
	return 5;
})
.then((val)=>{
	console.log(val); // 5
})

4. thenやcatchでreturnされた後は次のthenが実行され、catchはskipする

new Promise((resolve)=>resolve(1))
.then((val)=>{
	console.log(val); // 1
	return 2;
})
.then((val)=>{
	console.log(val); // 2
	return 3;
})
.catch(()=>{console.log('skip')}) // 実行されない
.then((val)=>{
	console.log(val); // 3
})

5. thenやcatchでthrowされた後は次のcatchが実行され、thenはskipする

new Promise((resolve)=>resolve(1))
.then((val)=>{
	console.log(val); // 1
	return 2;
})
.then((val)=>{
	console.log(val); // 2
	throw new Error(3);
})
.then(()=>{console.log('skip')}) // 実行されない
.catch((error)=>{
	console.log(error.message); // 3
}) 

6. throwされた後にcatchがいないとエラーが出る

new Promise((resolve)=>resolve(1))
.then((val)=>{
	console.log(val); // 1
	return 2;
})
.then((val)=>{
	console.log(val); // 2
	throw new Error(3);
})
.then(()=>{console.log('skip')});

// catchがいないのでエラーになる

7. finallyは順番が来たら実行されるが、値は受け取れない、その後のチェーンは続く

new Promise((resolve)=>resolve(1))
.then((val)=>{
	console.log(val); // 1
	return 2;
})
.then((val)=>{
	console.log(val); // 2
	throw new Error(3);
})
.finally((val)=>{
	console.log(val); // undefined
})
.catch((error)=>{
	console.log(error.message); // 3
}) 

8. thenやcatchの中でPromiseがreturnされたらそのPromiseのresolve,rejectが実行されるのを待ち、resolveなら次はthenをrejectなら次はcatchを実行する

new Promise((resolve)=>resolve(1))
.then((val)=>{
	console.log(val); // 1
	return 2;
})
.then((val)=>{
	return new Promise((resolve, reject)=>{
		setTimeout(()=>{
			resolve(3);
		}, 1000);
	})
})
.then((val)=>{
	console.log(val); // 1秒後に3が出力
	return new Promise((resolve, reject)=>{
		setTimeout(()=>{
			reject(new Error(4));
		}, 1000);
	})
})
.catch((error)={
	console.log(error.message); // 3が出力された1秒後に4が出力
})

Promiseの問題

ES2015で登場したのでそれ以前のwebAPIには対応していない

Promiseに対応した最新のwebAPIは内部的にPromiseを返してくれる

<例>

↓OSに繋がっているビデオやカメラを非同期で取得する

// このgetUserMediaメソッドはPromiseを返しているのでPromiseチェーンが使える
navigator.mediaDevices.getUserMedia({video:true})
.then((value)=>{
	console.log(value); // videoの情報が取れればその情報が入る
})
.catch((error)=>{
	console.log(error.message); // 情報が取れなければエラーオブジェクトが返される
})

↓上記の処理に加えてクリップ情報を取得したければ

navigator.mediaDevices.getUserMedia({video:true})
.then((value)=>{
	console.log(value); 
})
.catch((error)=>{
	console.log(error.message);
})
.then(()=>{
	return navigator.clipboard.readText(); // これもPromiseを返すのでresolveかrejectされるのを待つ
})
.then((text)=>{
	console.log(text); // 上記が内部的にresolveされたらクリップボードの値が取れる
})
.catch((error)=>{
	console.log(error.message); // rejectされたらこっち
})

古いAPIをPromise化する(Promisified)

setTimeoutをPromise化

let promisifiedSetTimeout = (time) => {
	return new Promise((resolve)=>{
		setTimeout(()=>{
			resolve();
		}, time);
	});
};

Promise化されたsetTimeoutを使う

promisifiedSetTimeout(1000)
.then(()=>{
	console.log(1);
	return promisifiedSetTimeout(1000);	
})
.then(()=>{
	console.log(2);
	return promisifiedSetTimeout(1000);	
})
.then(()=>{
	console.log(3);
	return promisifiedSetTimeout(1000);	
})