メインコンテンツまでスキップ

await

Promiseを利用した非同期処理をより簡単に書ける構文としてasync/awaitが存在します。
この構文を利用することで、非同期処理をより同期処理と同じような文脈で書くことができるようになります。
async/awaitは基本セットで使いますが、本ページではawaitを主に取り上げます。

await

awaitPromiseの値が解決されるまで実行を待機して、解決された値を返します。

awaitの注意点として基本的にawaitasync関数の中でしか使えません。

ts
// 1秒後に値を返す
function request(): Promise<string> {
return new Promise((resolve) => {
setTimeout(() => {
resolve("hello");
}, 1000);
});
}
 
// この書き方はできない
// const result = await request();
// console.log(result);
 
async function main() {
const result = await request();
console.log(result);
// @log: "hello"
}
 
main();
ts
// 1秒後に値を返す
function request(): Promise<string> {
return new Promise((resolve) => {
setTimeout(() => {
resolve("hello");
}, 1000);
});
}
 
// この書き方はできない
// const result = await request();
// console.log(result);
 
async function main() {
const result = await request();
console.log(result);
// @log: "hello"
}
 
main();

この例ではawait request()の行でrequest()Promiseを解決するまで1秒待機し、コンソールに"hello"と表示します。

async/awaitで書き直す

最後にみっつのAPI呼び出しのコードをasync/awaitを利用して書き直してみます。

このようにasync/awaitを利用することで、非同期の処理を同期処理のようにスッキリ書くことができるようになります。

ts
// 非同期でAPIにリクエストを投げて値を取得する処理
function request1(): Promise<number> {
return new Promise((resolve) => {
setTimeout(() => {
resolve(1);
}, 1000);
});
}
 
// 受け取った値を別のAPIにリクエストを投げて値を取得する処理
function request2(result1: number): Promise<number> {
return new Promise((resolve) => {
setTimeout(() => {
resolve(result1 + 1);
}, 1000);
});
}
 
// 受け取った値を別のAPIにリクエストを投げて値を取得する処理
function request3(result2: number): Promise<number> {
return new Promise((resolve) => {
setTimeout(() => {
resolve(result2 + 2);
}, 1000);
});
}
 
async function main() {
const result1 = await request1();
const result2 = await request2(result1);
const result3 = await request3(result2);
console.log(result3);
// @log: 4
}
 
main();
ts
// 非同期でAPIにリクエストを投げて値を取得する処理
function request1(): Promise<number> {
return new Promise((resolve) => {
setTimeout(() => {
resolve(1);
}, 1000);
});
}
 
// 受け取った値を別のAPIにリクエストを投げて値を取得する処理
function request2(result1: number): Promise<number> {
return new Promise((resolve) => {
setTimeout(() => {
resolve(result1 + 1);
}, 1000);
});
}
 
// 受け取った値を別のAPIにリクエストを投げて値を取得する処理
function request3(result2: number): Promise<number> {
return new Promise((resolve) => {
setTimeout(() => {
resolve(result2 + 2);
}, 1000);
});
}
 
async function main() {
const result1 = await request1();
const result2 = await request2(result1);
const result3 = await request3(result2);
console.log(result3);
// @log: 4
}
 
main();

Promiseを直接awaitする

関数を作らずにPromiseを直接awaitすることもできます。

ts
async function main() {
// 1秒後に値を返す
await new Promise((resolve) => {
setTimeout(() => resolve, 1000);
});
}
ts
async function main() {
// 1秒後に値を返す
await new Promise((resolve) => {
setTimeout(() => resolve, 1000);
});
}

async関数をawaitする

async関数をawaitすることもできます。

ts
async function request(): Promise<string> {
return "hello";
}
 
async function main() {
const result = await request();
console.log(result);
// @log: "hello"
}
ts
async function request(): Promise<string> {
return "hello";
}
 
async function main() {
const result = await request();
console.log(result);
// @log: "hello"
}

awaitしたときの型注釈

Promise, async関数の戻り値の型注釈はPromise<T>Tになります。

ts
async function request(): Promise<string> {
return "hello";
}
 
async function main() {
const result: string = await request();
// stringになる
console.log(result);
// @log: "hello"
}
ts
async function request(): Promise<string> {
return "hello";
}
 
async function main() {
const result: string = await request();
// stringになる
console.log(result);
// @log: "hello"
}

then-catchtry-catchに書き換える

Promisethencatchtry-catchに書き換えることができます。次のmain2関数はmain1関数をtry-catchで書き換えたものです。

ts
async function request(): Promise<string> {
return "hello";
}
 
function main1() {
request()
.then((result: string) => {
console.log(result);
// @log: "hello"
})
.catch((error: unknown) => {
console.log(error);
});
}
 
async function main2() {
try {
const result: string = await request();
console.log(result);
// @log: "hello"
} catch (error: unknown) {
console.log(error);
}
}
ts
async function request(): Promise<string> {
return "hello";
}
 
function main1() {
request()
.then((result: string) => {
console.log(result);
// @log: "hello"
})
.catch((error: unknown) => {
console.log(error);
});
}
 
async function main2() {
try {
const result: string = await request();
console.log(result);
// @log: "hello"
} catch (error: unknown) {
console.log(error);
}
}

拒否されたPromisereturnするとき

returnの前にawaitする

拒否されたPromisereturnする前にawaitしたときは、その関数内で例外が発生します。

ts
async function request(): Promise<unknown> {
throw new Error("error");
}
 
async function main(): Promise<unknown> {
try {
// return await とすることでcatchで例外を捕捉できる
return await request();
} catch {
console.log("error");
// @log: error
} finally {
console.log("finally");
// @log: finally
}
}
 
main()
.then(() => {
console.log("then");
// @log: then
})
.catch(() => {
console.log("catch");
});
ts
async function request(): Promise<unknown> {
throw new Error("error");
}
 
async function main(): Promise<unknown> {
try {
// return await とすることでcatchで例外を捕捉できる
return await request();
} catch {
console.log("error");
// @log: error
} finally {
console.log("finally");
// @log: finally
}
}
 
main()
.then(() => {
console.log("then");
// @log: then
})
.catch(() => {
console.log("catch");
});

このような例であれば表示されるものはerrorfinally、そしてthenが表示されます。

returnの前にawaitしない (ただreturnする)

拒否されたPromiseをそのまま関数の戻り値にしてしまうと拒否されたまま呼び出し元に戻されます。

ts
function request(): Promise<unknown> {
throw new Error("error");
}
 
// try -> finally -> return -> catch()
async function main(): Promise<unknown> {
try {
return request();
} catch {
console.log("error");
} finally {
console.log("finally");
// @log: finally
}
}
 
main()
.then(() => {
console.log("then");
})
.catch(() => {
console.log("catch");
// @log: catch
});
ts
function request(): Promise<unknown> {
throw new Error("error");
}
 
// try -> finally -> return -> catch()
async function main(): Promise<unknown> {
try {
return request();
} catch {
console.log("error");
} finally {
console.log("finally");
// @log: finally
}
}
 
main()
.then(() => {
console.log("then");
})
.catch(() => {
console.log("catch");
// @log: catch
});

このような例であれば表示されるものはfinallycatchが表示されます。