5
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

JavaScript Promiseの実行タイミング Promise.start() とか Promise.execute() 的なもの

Last updated at Posted at 2021-01-25

Promiseには .start() .execute() が存在しない

Promise objectを作ってtaskを設置したとき、発火方法がわからなかった。

イメージとしては ↓ だったんだけど

const task = new Promise(()=>{ // task});
task.start() // こんなものはない

Promiseにそんなmethodはなかった。使えるのは6つだけ。

  1. Promise.all(promises)
  1. Promise.allSettled(promises)
  2. Promise.race(promises)
  3. Promise.any(promises)
  4. Promise.resolve(value)
  5. Promise.reject(error)

https://javascript.info/promise-api

Promiseは Object を作った瞬間に start() するものだった!!

  1. 関数の中なら、関数が呼ばれると、Promise objectが生成される = 実行される
  2. MAIN関数や <script> の直下なら file loadした瞬間に Promise objectが生成される = 即実行される

発火のコントロール方法

まずPromiseの宣言方法(というか、使い方) は2つ。

  1. Object(変数)として作る
  2. 関数の中に作る(これを個人的には 関数として作る と言いたい)

↓では最初の3つ(a, b, c)が変数、残りが関数(d, e) で書いている。

<script>

// awaitを中で使うので、この start() は async で宣言する
const start = async () => {

	//////////////////////////////////////////////////////
	// (a) objectだと宣言した瞬間に実行される
	const objImmediately = new Promise((resolve) => {
		resolve();
		console.log("(a) objImmediately done");
	});

	// (b) objectだと宣言した瞬間に実行される
	const objImmediatelyWithSleep = new Promise((resolve) => {
		setTimeout(()=>{
			resolve();
			console.log("(b) objImmediatelyWithSleep done");
		}, 400)
	});

	// (c) object宣言とともに実行 & awaitでこれより下の行を一時停止する
	const objImmediatelyAndAwait = await new Promise(async (resolve) => {
		await setTimeout(async ()=>{
			resolve();
			console.log("(c) objImmediatelyAndAwait done");
		}, 300)
	});


	// (d) 関数の中で Promise objectを作る。生成即実行かと思いきや、関数が呼ばれるまでは実行されない。
	const funcLater = async () => {
		await new Promise(async (resolve) => {
			setTimeout(()=>{
				resolve();
				console.log("(d) funcLater done");
			}, 200)
		});
	}

	// (e) これも関数の中なので即実行されない。Promiseをreturnすると then が使える
	const funcLaterReturnPromise = async () => {
		return await new Promise((resolve) => {
			setTimeout(()=>{
				resolve();
				console.log("(e) funcLaterReturnPromise done");
			}, 100)
		});
	}


	// MAIN ////////////////////////////////////////////////////

	// (a, b, c) objectは関数ではないのでできない & 不要
	// objImmediately()
    // objImmediatelyWithSleep()
	// objImmediatelyAndAwait()


	// (d) 関数を呼ぶ = promiseが初めて実行される
	await funcLater();

	// 返り値がないので then はできない
	// await funcLater().then(()=>{console.log("then")});
	// Uncaught (in promise) TypeError: Cannot read property 'then' of undefined

	// (e) 返り値がpromiseだとチェーンできる
	await funcLaterReturnPromise()
	.then(
		()=>{console.log("(e) then")
	});

	console.log("(f) Finished.");
}

start();
</script>

近頃は猫も杓子も promise をreturnしてくるので、async関数じゃないとコールすらできなかったりする。
(start()関数がわざわざ async function になってるのは中で awaitを使ってるから)

result

(a) objImmediately done
(c) objImmediatelyAndAwait done
(b) objImmediatelyWithSleep done
(d) funcLater done
(e) funcLaterReturnPromise done
(e) then
(f) Finished.

発火順 と 完了順

x = 発火
o = 実行中

image.png

objectの3つは宣言 即 実行されている。

関数の2つはその後で call されている。

発火順は上から下に a-e だ。
だけど、awaitが絡んで完了順は C が B を追い越している。その他は順番通り。
肝は、d&eのsleepがb&cより短いのに、d&eはb&cより後に完了する点だ。これは単純に発火を遅らせて実現している。

(c) がobjectだけどawaitしているので、(d)は (c) が終わるまで待っている。

(e) は (d) が終わるまで待っている。

まとめ

  1. Promise を objectとしてコールすると、即実行される。
  2. Promiseの発火タイミングをコントロールしたかったら、関数の中で宣言する。 関数をいつ呼ぶか でコントロールする。

直接 new Promise してるコードを見たら その関数がいつ呼ばれたか を知らないと即実行されたタイミングがわからない。外側の関数を見に行く必要がある。しかし大概そこでも new Promiseされている。さらに外側の関数を見に行くことになる。

僕らは無限のPromise地獄の中でもがいているのだ。

5
5
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?