sumakokima2 / resium-sample2

0 stars 0 forks source link

async/await #14

Open sumakokima2 opened 5 years ago

sumakokima2 commented 5 years ago

saveImageは非同期関数なので、ここで画像保存完了まで待ってくれません。saveImageがPromiseを返すようにして、async/awaitを使う方が良いでしょう。

sumakokima2 commented 5 years ago

asyncとは

非同期関数を定義する関数宣言のこと。

async functionは呼び出されるとPromiseを返す。 async functionが値をreturnした場合、Promiseは戻り値をresolveする。 async functionが例外や何らかの値をthrowした場合はその値をrejectする。

awaitとは

awaitを指定した関数のPromiseの結果が返されるまで、async function内の処理を一時停止する。 結果が返されたらasync function内の処理を再開する。 awaitはasync function内で利用

Promise

new Promise(function (resolve, reject) {
  非同期的な関数({
    成功時: (成果) => resolve(成果)
    失敗時: (問題) => reject(問題)
  })
})

以下、コードを用いて学んでみる

export const saveimage = async (options: {   // ここで用いる引数の宣言。 id: string; video: { isYoutube: boolean; id: string } | undefined; photo: string | undefined; skip: (filename: string) => boolean;   //ここの書き方がわかりません }) => { const filename = peacenippon_icon_${options.id}.png;  //${ }は変数代入の書き方

if (options.skip(filename)) {      //まず、booleanがfalseだったら以降何もしない return undefined; }

const imageurl = options.video    //ビデオタイプの判定 ? options.video.isYoutube ? http://i.ytimg.com/vi/${options.video.id}/default.jpg : https://i.vimeocdn.com/video/${options.video.id}_640.webp : options.photo;

if (!imageurl) return;         //imageurlが何もなかったら何もしない

return download(imageurl, path.join("data", filename));  //上記でreturnに引っかからなかったら(非同期処理として)(ここまでが終わったら)download関数を実行(①) };

export const download = async (url: string, filename: string) => { //② console.log([${new Date().toISOString()}] image: download from ${url});

//ここから先明日やります。

const res = await fetch(url).catch(err => { throw err; });

if (!res || !res.ok || !res.body) throw new Error("failed to fetch image");

return new Promise((resolve, reject) => { const stream = imagemagick(res.body, path.basename(url)) //streamに一時保存 .resize(112, 82, "^") .crop(112, 82) .extent(120, 90) .in("-background", "none") .in("-gravity", "Center") .noProfile() .stream("png");

((imagemagick(stream, filename) as any).composite("frame.png") as gm.State)
  .in("-background", "none")
  .in("-gravity", "Center")
  .in("-compose", "Over")
  .resize(48)
  .write(filename, err2 => {
    if (!err2) {
      resolve(filename);
    } else {
      reject(err2);
    }
  });

}); };

rot1024 commented 5 years ago

new Promise は、コールバックを受け取るPromiseでない非同期関数をPromise化するのに使います。使いたい関数がPromiseを返すようになっている場合は不要です。

sumakokima2 commented 5 years ago

質問について

catch内で例外がスローされると、例外は1個外側に伝播していきます。したがってawaitで待っているところでthrowするのと同じ効果になります。 throwされた場所がasync関数内なので、その関数はRejectされたPromiseを返すようになります。 つまりそのasync関数を使っているところでcatchが繋がっていればそちらが実行されます。 なおawaitしている場所でthrowされるのと同じということは、

try { const res = await fetch(url); } catch (e) { 例外処理 }

のようにもできます。この場合awaitから先には進まず、catch文内の例外処理が呼ばれます。 ネストが一段深くなるという欠点もあり、ここではコードの簡潔さを重視してPromise.prototype.catchで書く書き方をしています。 ちなみにcatchもthenの二番目のバラメータの指定もなにもないpromiseで例外が起きた場合、そのPromiseはrejectされた状態にはなりますが、プログラムは停止せず、何事もなかったかのようになり、エラーがチェックされないまま握りつぶされてしまう恐れがあります。最近の処理系では、そのようなことが起こると自動的に警告が表示されるようになっているものが多いです。

sumakokima2 commented 5 years ago

Promise Constructor

var promise1 = new Promise(function(resolve, reject) {
  setTimeout(function() {
    resolve('foo');
  }, 300);
});

promise1.then(function(value) {
  console.log(value);
  // expected output: "foo"
});

console.log(promise1);
// expected output: [object Promise]

実行結果

> [object Promise]
> "foo"

Promise の状態は以下のいずれかとなります。

pending: 初期状態。成功も失敗もしていません。 fulfilled: 処理が成功して完了したことを意味します。 rejected: 処理が失敗したことを意味します。

pending 状態の Promise は、何らかの値を持つ fulfilled 状態、もしくは何らかの理由 (エラー) を持つ rejected 状態のいずれかに変わります。そのどちらとなっても、then メソッドによって関連付けられたハンドラーが呼ばれます。(https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise)

Promise.prototype.then() メソッド:成功時の処理。プロミスに成功ハンドラと失敗ハンドラを付加します。呼ばれたハンドラの戻り値によって解決している新しいプロミスを返します。 Promise.prototype.catch() メソッド:エラー時の処理。プロミスに失敗ハンドラコールバックを付加します。呼ばれるとコールバックの戻り値、または、プロミスが代わりに満たされているなら、オリジナル成功値によって完了している新しいプロミスを返します。

sumakokima2 commented 5 years ago

throw 文は、ユーザー定義の例外を投げます。現在の関数の実行を止めて (throw の後の文は実行しません)、コールスタック内の最初の catch ブロックに制御を移します。呼び出し元の関数に catch ブロックが存在しない場合は、プログラムが終了します。

Error オブジェクトの活用セクション(new Error) エラーの種類に応じてより手の込んだメッセージが得られるように、'name' および 'message' プロパティを使うことができます。'name' はエラーの一般的なクラス(例えば 'DOMException' や 'Error')を表し、一方 'message' は通常、Error オブジェクトを文字列に変換したものより簡潔なメッセージを表します。

new Error([message[, fileName[, lineNumber]]])

throw文を使うとユーザーが例外を投げることができます。 例外として投げられたオブジェクトは、catch節で関数の引数のようにアクセスできます。 catch節でオブジェクトを参照できる識別子を例外識別子と呼びます。

try {
    // 独自の例外を投げる
    throw new Error("例外が投げられました");
} catch (error) {
    // catch節のスコープでerrorにアクセスできる
    console.log(error.message); // => "例外が投げられました"
}

たぶん、このあたりのerrorの理解は、実際に使って行ってわかるものだと感じました。