LoginSignup
6
2

More than 5 years have passed since last update.

async-awaitの落とし穴(と解決策)

Last updated at Posted at 2017-10-05

さて問題です。

以下のようなコードがあります。


async allTask() {
     let task1Results = await task1();
     await task2();
     task1Results.map(async result => task3(result));
}

こちらとしてはtask1とtask2とtask3が逐次的に実行してくれることを期待していますが、
コレはそのように仕事をしてくれるのでしょうか?

してくれませんでした。 task1Results.map(async result => task3(result));
task2を待たずに非同期で実行されてしまいます。

async allTask() {
     let task1Results = await task1();
     await task2();
     await Promise.all(task1Results.map(async result => task3(result)));
}

ちゃんと await promise.allをつけないとだめです。気づかなくて2h消費しました

だめな例


async function sleep(time: number) {
    return new Promise<void>(resolve => {
      setTimeout(() => (resolve()), time);
    });
  }


  async function task1() {
    await sleep(1000);
    console.log(`task1 end`);
    return new AsyncArray(0,1,2,3,4,5);
  }

  async function task2() {
    await sleep(3000)
    console.log(`task2 end`);
  }

  async function task3(num:number) {
    await sleep(num * 1000);
    console.log(`task3(${num}) end`);
  }

  async function allTask() {
    let results =  await task1();
    await task2();
    await results.asyncMap(async num => task3(num));
  }

  allTask().then(() => console.log('allTask end'));

output
task1 end
task2 end
allTask end
task3(0) end
task3(1) end
task3(2) end
task3(3) end
task3(4) end
task3(5) end

改善1

async function sleep(time: number) {
    return new Promise<void>(resolve => {
      setTimeout(() => (resolve()), time);
    });
  }


  async function task1(): Promise<number[]> {
    await sleep(1000);
    console.log(`task1 end`);
    return [0,1,2,3,4,5];
  }

  async function task2() {
    await sleep(3000)
    console.log(`task2 end`);
  }

  async function task3(num:number) {
    await sleep(num * 1000);
    console.log(`task3(${num}) end`);
  }

  async function allTask() {
    let results =  await task1();
    await task2();
    await Promise.all(results.map(async num => task3(num)));
  }

  allTask().then(() => console.log('allTask end'));

output
task1 end
task2 end
task3(0) end
task3(1) end
task3(2) end
task3(3) end
task3(4) end
task3(5) end
allTask end

改善2

export class AsyncArray<T> extends Array<T> {
    async asyncFilter(compare: (value: T) => Promise<boolean>) {
      const results = await Promise.all(this.map(v => compare(v)));
      return <AsyncArray<T>>this.filter((_, i) => results[i]);
    }

    async asyncMap<U>(convert: (value: T) => Promise<U>) {
      return <Array<U>> await Promise.all(this.map(convert));
    }
  }

  async function sleep(time: number) {
    return new Promise<void>(resolve => {
      setTimeout(() => (resolve()), time);
    });
  }


  async function task1() {
    await sleep(1000);
    console.log(`task1 end`);
    return new AsyncArray(0,1,2,3,4,5);
  }

  async function task2() {
    await sleep(3000)
    console.log(`task2 end`);
  }

  async function task3(num:number) {
    await sleep(num * 1000);
    console.log(`task3(${num}) end`);
  }

  async function allTask() {
    let results =  await task1();
    await task2();
    await results.asyncMap(async num => task3(num));
  }

  allTask().then(() => console.log('allTask end'));

output
task1 end
task2 end
task3(0) end
task3(1) end
task3(2) end
task3(3) end
task3(4) end
task3(5) end
allTask end
6
2
0

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
6
2