javascriptのPromiseをpythonで実装する方法


# Motive
`selenium` でスマホ版でブラウザを起動しようと思った時、

```Python:
    sp_args = "--user-agent="
    sp_args_list = ["Mozilla/5.0 (iPhone; CPU iPhone OS 10_2 like Mac OS X)",
                    "AppleWebKit/602.3.12 (KHTML, like Gecko)",
                    "Version/10.0 Mobile/14C92 Safari/602.1"]
    sp_args += " ".join(sp_args_list)
```
と設定するだけでは一部のサイトではレスポンシブになっているため対応しない場合があります。

そのため、 `width` を450くらいにしてリロードをするとスマホ版の表示になるのですが、`ブラウザ設定 → スマホ画面の大きさを設定 -> リロード` の順に一つのプロセスが完了したことがcompleteになった状態で次のプロセスに移す実装ができないかと思ったわけです。

で、 javascriptの非同期処理で使われる`Promise`と同等のものがpythonでできないかと考えたわけっす。


# Preparetion
`pip` で探すと早速パッケージが見つかりました。ふつーに

```
pip install Promise
```

で良いです。

# Model
参考にしたサンプルコードですが[初めてのJavaScript 第3版 ――ES2015以降の最新ウェブ開発 14章 非同期プログラミング](https://www.oreilly.co.jp/books/9784873117836/)にあったロケット発射モデルを題材にしました。

元コードからブラウザに表示する編集等々でちょっと変えています。


```html

<html>
	<head>
		<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
		<script type="text/javascript" src="./sample.js"></script>
	</head>
	<body>
		<h3>apolonX</h3>
		<div class="box">
		</div>
	</body>

</html>

```

``` JavaScript:
$(()=>{

	countdown(10)
	.then(()=>{
		return new Promise((resolve, reject) => {
			setTimeout(() => {
				//大気圏突入
				resolve(insertBox("atmosphere..."));
			}, 2000);
		});
	})
	.then(()=>{
		//成功
		insertBox("success!!");
	});

});

//カウントダウン
function countdown(seconds){
	let dst = new Promise(
			(resolve, reject) => {
				for (let i=seconds;0 <=i;i--){
					setTimeout(() => {
						if(i>0){
							insertBox(countSign(i));
						} else {
							resolve(insertBox(goSign()));
						}
					}, (seconds-i)*1000);
				}
			}
		);
	return dst
}


function insertBox(src){
	let p = $("<p>")
	p.text(src);
	$(".box").append(p);
}

function countSign(index){
	return index + "...";
}

function goSign(){
	return "GO!";
}
```

で約2秒ごとにブラウザにメッセージが表示されます。

![count.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288206/8b7f1924-a442-b7ad-88c2-3aa9f93d8560.gif)


# Method

```Python
from promise import Promise
import time

def main():
    countdown(10).then(go()).then(Promise.resolve(atmosphere())).then(success())

def countdown(seconds):
    def myfunc(resolve, reject):
        for i in reversed(range(0,seconds + 1)):
            if 0 < i:
                print( str(i) + "..." )
                time.sleep(2)
            else :
                resolve()
    return Promise(myfunc)

def go():
    time.sleep(1)
    print("GO")


def atmosphere():
    time.sleep(1)
    print("atmosphere")

def success():
    print("success!!")


if __name__ == '__main__':
	main()

```

`countdown(10).then(go()).then(Promise.resolve(atmosphere())).then(success())
` でチェイン化しています。

![Aug 4 2019 1_22 PM - Edited.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288206/7a885505-10bf-cf98-bee4-9c13e46c5b9a.gif)


# Future
複数のAPIでデータ取得後に一括処理して文章生成するなどのケースであったら、全てのAPIのデータ取得後に一括処理する一貫性が保たれるので何かと良い。
簡易的なトランザクション処理ですかね。

# Reference
[promise](https://pypi.org/project/promise/#history)
[promise:github](https://github.com/syrusakbary/promise)
[初めてのJavaScript 第3版 ――ES2015以降の最新ウェブ開発 14章 非同期プログラミング](https://www.oreilly.co.jp/books/9784873117836/)
[Python の Promise 実装とその活用方法について / How to implement Promise by Python and how to use it](http://datalove.hatenadiary.jp/entry/python/how-to-implement-promise-by-python)