Array.map과 함께 async await 사용


170

다음 코드가 주어진다 :

var arr = [1,2,3,4,5];

var results: number[] = await arr.map(async (item): Promise<number> => {
        await callAsynchronousOperation(item);
        return item + 1;
    });

다음과 같은 오류가 발생합니다.

TS2322 : 'Promise <number> []'유형은 'number []'유형에 할당 할 수 없습니다. 'Promise <number> 유형은'number '유형에 할당 할 수 없습니다.

어떻게 고칠 수 있습니까? 어떻게 할 수 async awaitArray.map작업을 함께?


6
동기 작업을 비동기 작업으로 만드는 이유는 무엇입니까? arr.map()동기식이며 약속을 반환하지 않습니다.
jfriend00

2
동기 작업을 map예상하고 작동 할 것으로 예상되는 함수에 비동기 작업을 보낼 수 없습니다 .
Heretic Monkey

1
@ jfriend00 내부 함수에 많은 대기 명령문이 있습니다. 실제로 긴 기능이며 읽을 수 있도록 단순화했습니다. 비동기식이어야하는 이유를 분명히하기 위해 대기 호출을 추가했습니다.
Alon

배열을 반환하는 것이 아니라 약속을 반환하는 것을 기다려야합니다.
jfriend00

2
한 가지 유용한 사실은 함수를로 표시 할 때마다 async해당 함수가 약속을 반환하게한다는 것입니다. 그래서 물론, 비동기의지도 : 약속의 배열을 반환
안토니 매닝 - 프랭클린

답변:


380

여기서 문제 await는 약속이 아닌 다양한 약속을 시도 하고 있다는 것입니다. 이것은 당신이 기대하는 것을하지 않습니다.

전달 된 객체 await가 Promise가 아닌 await경우 값을 해결하려고하지 않고 그대로 값을 반환합니다. 따라서 awaitPromise 대신 Promise 객체의 배열 을 전달했기 때문에 await에 의해 반환되는 값은 단순히 해당 유형의 배열 Promise<number>[]입니다.

여기서해야 할 일은 Promise.all배열 map을 단일 Promise로 변환하기 위해 반환 된 배열을 호출 await하는 것입니다.

에 따르면 대한 MDN 워드 프로세서Promise.all :

Promise.all(iterable)메소드는 반복 가능한 인수의 모든 약속이 해결되거나 거부 된 첫 번째 전달 된 약속의 이유로 거부되는 약속을 리턴합니다.

따라서 귀하의 경우 :

var arr = [1, 2, 3, 4, 5];

var results: number[] = await Promise.all(arr.map(async (item): Promise<number> => {
    await callAsynchronousOperation(item);
    return item + 1;
}));

이렇게하면 여기에서 발생하는 특정 오류가 해결됩니다.


1
무엇을 할 :콜론은 평균?
다니엘은

11
@DanielPendergast TypeScript의 타입 주석 용입니다.
Ajedi32

비동기 맵 함수 내부 callAsynchronousOperation(item);await외부에서 호출 하는 것의 차이점은 무엇입니까 ?
nerdizzle

@nerdizzle 다른 질문에 대한 좋은 후보처럼 들립니다. 그러나 기본적 으로이 await기능을 사용하면 비동기 작업이 완료되기를 기다리거나 계속하지 않고 기다리지 않고 기다리지 않고 즉시 계속 진행합니다.
Ajedi32

응답은 @ Ajedi32 thx입니다. 그러나 비동기 맵에서 기다림이 없으면 더 이상 함수의 결과를 기다릴 수 없습니까?
nerdizzle

15

네이티브 약속을 사용하지 않고 블루 버드를 사용하는 경우 다른 해결책이 있습니다.

Promise.map ()을 사용 하여 array.map과 Promise.all을 혼합 할 수도 있습니다

당신의 경우 :

  var arr = [1,2,3,4,5];

  var results: number[] = await Promise.map(arr, async (item): Promise<number> => {
    await callAsynchronousOperation(item);
    return item + 1;
  });

2
그것은 다릅니다-모든 작업을 병렬로 실행하는 것이 아니라 순서대로 실행합니다.
Andrey Tserkus

5
@AndreyTserkus Promise.mapSeries또는 Promise.eachsequencial Promise.map은 한 번에 시작합니다.
Kiechlus

1
@AndreyTserkus concurrency옵션 을 제공하여 전체 또는 일부 작업을 병렬로 실행할 수 있습니다 .

11
그것이 바닐라 JS가 아니라는 것을 언급 할 가치가 있습니다.
Michal

@Michal 네, SyntaxError입니다
CS QGB


2

위에서 언급 한대로 Promise.all을 사용하는 것이 좋지만 실제로 그러한 접근 방식을 피하고 싶다면 for 또는 다른 루프를 수행 할 수 있습니다.

const arr = [1,2,3,4,5];
let resultingArr = [];
for (let i in arr){
  await callAsynchronousOperation(i);
  resultingArr.push(i + 1)
}

6
Promise.all은 배열의 각 요소에 대해 비동기 적입니다. 이것은 동기화 될 것입니다. 다음 요소를 시작하려면 한 요소를 끝내기를 기다려야합니다.
산티아고 멘도사 라미레즈

이 방법을 시도하는 사람들에게는 for..of가 배열 내용을 반복하는 적절한 방법이며 for..of는 인덱스를 반복합니다.
ralfoide

2

아래의 해결 방법은 배열의 모든 요소를 ​​비동기 적으로 처리하고 순서를 유지합니다.

const arr = [1, 2, 3, 4, 5, 6, 7, 8];
const randomDelay = () => new Promise(resolve => setTimeout(resolve, Math.random() * 1000));

const calc = async n => {
  await randomDelay();
  return n * 2;
};

const asyncFunc = async () => {
  const unresolvedPromises = arr.map(n => calc(n));
  const results = await Promise.all(unresolvedPromises);
};

asyncFunc();

또한 codepen .

Promise.all에 대해서만 "기다리십시오". "대기"없이 여러 번 calc를 호출하고, 해결되지 않은 약속을 즉시 수집합니다. 그런 다음 Promise.all은 모든 해상도가 해결 될 때까지 기다렸다가 해결 된 값을 순서대로 배열로 반환합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.