programing

루프에 대한 JavaScript ES6 약속

magicmemo 2023. 7. 25. 20:50
반응형

루프에 대한 JavaScript ES6 약속

for (let i = 0; i < 10; i++) {
    const promise = new Promise((resolve, reject) => {
        const timeout = Math.random() * 1000;
        setTimeout(() => {
            console.log(i);
        }, timeout);
    });

    // TODO: Chain this promise to the previous one (maybe without having it running?)
}

위의 명령은 다음과 같은 랜덤 출력을 제공합니다.

6
9
4
8
5
1
7
2
3
0

약속 ..then()).

어떤 이유에서인지, 저는 그것을 할 방법을 찾을 수 없었습니다.

기능을 .yield), 약속을 반환하는 간단한 함수를 시도했지만 결국에는 항상 같은 문제로 귀결됩니다.루프가 동기식입니다.

비동기식을 사용하면 간단히 사용할 수 있습니다.async.series().

어떻게 해결합니까?

이미 질문에서 암시했듯이 코드는 모든 약속을 동기화하여 만듭니다.대신 앞의 문제가 해결될 때만 생성해야 합니다.

두 번째로, 각각의 약속은 다음과 같이 만들어집니다.new Promise.resolve(또는)reject되면 이 해야 합니다.) 타이머가 만료되면 이 작업을 수행해야 합니다.그것은 모든 것을 촉발할 것입니다.then그 약속에 대해 당신이 가질 콜백. 그고그런.then 콜또(으)로 표시)await는 요소입니다.)는 체인을 구현하기 위한 필수 요소입니다.

이러한 구성 요소를 사용하여 이 비동기 체인을 수행하는 몇 가지 방법이 있습니다.

  1. 과 함께for 해결 약속으로 ▁that시▁loop루.

  2. 와 함께Array#reduce합니다.

  3. 자체를 해결 콜백으로 전달하는 함수를 사용

  4. ECMA스크립트 2017의 / 구문 사용

  5. ECMA스크립트2020의 구문 사용

하지만 먼저 매우 유용하고 일반적인 기능을 소개하겠습니다.

setTimeout

용사를 합니다.setTimeout타이머가 만료되면 해결할 약속이 필요합니다. 약속이 필요합니다.그래서 이런 함수를 만들어 봅시다. 이것을 약속하는 함수라고 합니다. 이 경우 우리는 약속할 것입니다.setTimeout코드의 가독성이 향상되며 위의 모든 옵션에 사용할 수 있습니다.

const delay = ms => new Promise(resolve => setTimeout(resolve, ms));

아래의 각 옵션에 대한 토막글과 설명을 참조하십시오.

함포와 for

사용할 수 있습니다.for루프, 그러나 모든 약속을 동시에 만들지 않아야 합니다.대신 초기 즉시 해결 약속을 만든 다음 이전 약속이 해결하는 대로 새 약속을 연결합니다.

const delay = ms => new Promise(resolve => setTimeout(resolve, ms));

for (let i = 0, p = Promise.resolve(); i < 10; i++) {
    p = p.then(() => delay(Math.random() * 1000))
         .then(() => console.log(i));
}

그래서 이 코드는 하나의 긴 체인을 만듭니다.then 화. 수. 변. 통.p이는 해당 체인의 추적을 잃지 않고 동일한 체인에서 루프의 다음 반복을 허용하는 역할만 합니다.동기 루프가 완료된 후 콜백이 실행되기 시작합니다.

은 중한것은입니다.then-콜백은 다음과 같은 약속을 반환합니다.delay()생성: 비동기 체인을 보장합니다.

함포와 reduce

이는 이전 전략에 대한 보다 기능적인 접근 방식일 뿐입니다.실행할 체인과 동일한 길이의 어레이를 생성하고 즉시 확인 약속으로 시작합니다.

const delay = ms => new Promise(resolve => setTimeout(resolve, ms));

[...Array(10)].reduce( (p, _, i) => 
    p.then(() => delay(Math.random() * 1000))
     .then(() => console.log(i))
, Promise.resolve() );

이것은 약속에 사용할 데이터가 있는 어레이가 실제로 있을 때 더 유용할 것입니다.

해결 콜백으로 전달되는 함수 사용

여기서 함수를 만들고 즉시 호출합니다.첫 번째 약속을 동기화하여 만듭니다.이 문제가 해결되면 기능이 다시 호출됩니다.

const delay = ms => new Promise(resolve => setTimeout(resolve, ms));

(function loop(i) {
    if (i >= 10) return; // all done
    delay(Math.random() * 1000).then(() => {
        console.log(i);
        loop(i+1);
    });
})(0);

이렇게 하면 다음과 같은 이름의 함수가 생성됩니다.loop코드의 맨 끝에서 0번 인수와 함께 즉시 호출되는 것을 볼 수 있습니다.이것이 카운터이고, i 인수입니다.이 기능은 카운터가 여전히 10 미만이면 새 약속을 생성하고, 그렇지 않으면 체인이 중지됩니다.

delay()"" " " " " " " 을 .then함수를 다시 호출하는 콜백.

함포와 async/await

현대의 JS 엔진은 다음과 같은 구문을 지원합니다.

const delay = ms => new Promise(resolve => setTimeout(resolve, ms));

(async function loop() {
    for (let i = 0; i < 10; i++) {
        await delay(Math.random() * 1000);
        console.log(i);
    }
})();

약속이 동시에 만들어지는 것처럼 보이기 때문에 이상하게 보일 수 있지만, 실제로는async함수는 첫 번째를 실행할 때 반환됩니다.await대기 중인 약속이 해결될 때마다 함수의 실행 컨텍스트가 복원되며, 다음 작업이 완료된 후 계속 진행됩니다.await루프가 완료될 때까지 계속됩니다.

함포와 for await...of

EcmaScript 2020을 통해 최신 JavaScript 엔진으로 전환되었습니다.이 경우 코드를 실제로 줄이지는 않지만 랜덤 간격 체인의 정의를 실제 반복으로부터 분리할 수 있습니다.

const delay = ms => new Promise(resolve => setTimeout(resolve, ms));

async function * randomDelays(count, max) {
    for (let i = 0; i < count; i++) yield delay(Math.random() * max).then(() => i);
}

(async function loop() {
    for await (let i of randomDelays(10, 1000)) console.log(i);
})();

사용할 수 있습니다.async/await을 해 도 아닙니다.제가 더 설명해 드리겠지만, 실제로는 아무 것도 없습니다.그냥 단골입니다.for 루를추습니다했가프▁the를 추가했습니다.await의 Promise 전 입니다.

제가 이것에 대해 좋아하는 것은 당신의 약속이 코드(또는 여기에 있는 다른 답변)가 포함하는 것과 같은 부작용 대신 정상적인 값을 해결할 수 있다는 것입니다.이렇게 하면 Zelda의 전설: 과거로의 연결과 같이 빛의 세계와 어둠의 세계 모두에 영향을 줄 수 있습니다. 즉, 깊게 중첩된 기능, 다른 다루기 힘든 제어 구조 또는 멍청한 라이프에 의존하지 않고도 약속된 데이터를 사용하기 전/후에 데이터로 쉽게 작업할 수 있습니다.

// where DarkWorld is in the scary, unknown future
// where LightWorld is the world we saved from Ganondorf
LightWorld ... await DarkWorld

그래서 이것이 어떻게 보일 것인지는 것입니다.

async function someProcedure (n) {
  for (let i = 0; i < n; i++) {
    const t = Math.random() * 1000
    const x = await new Promise(r => setTimeout(r, t, i))
    console.log (i, x)
  }
  return 'done'
}

someProcedure(10)
  .then(console.log)
  .catch(console.error)

0 0
1 1
2 2
3 3
4 4
5 5
6 6
7 7
8 8
9 9
done

귀찮은 일을 되는지 ..then우리 절차 내에서 전화를 걸 수 있습니까?async키워드는 자동으로 다음을 보장합니다.Promise반환되어, 우리는 a를 연결할 수 있습니다..then반환된 값을 호출합니다.이것은 우리가 큰 성공을 거두도록 도와줍니다: 다음의 순서를 실행합니다.n약속을 한 다음 성공/오류 메시지를 표시하는 등 중요한 작업을 수행합니다.

trincot의 훌륭한 답변을 바탕으로 배열의 각 항목에서 실행할 핸들러를 받아들이는 재사용 가능한 기능을 작성했습니다.함수 자체는 루프가 완료될 때까지 기다릴 수 있는 약속을 반환하며, 전달한 핸들러 함수도 약속을 반환할 수 있습니다.

루프(항목, 핸들러) : 약속

제가 맞추는데 시간이 좀 걸렸지만, 다음 코드는 많은 약속 루프 상황에서 사용할 수 있을 것이라고 생각합니다.

복사 붙여넣기 준비 코드:

// SEE https://stackoverflow.com/a/46295049/286685
const loop = (arr, fn, busy, err, i=0) => {
  const body = (ok,er) => {
    try {const r = fn(arr[i], i, arr); r && r.then ? r.then(ok).catch(er) : ok(r)}
    catch(e) {er(e)}
  }
  const next = (ok,er) => () => loop(arr, fn, ok, er, ++i)
  const run  = (ok,er) => i < arr.length ? new Promise(body).then(next(ok,er)).catch(er) : ok()
  return busy ? run(busy,err) : new Promise(run)
}

사용.

이를 사용하려면 첫 번째 인수로 루프오버할 배열과 함께 호출하고 두 번째 인수로 핸들러가 작동합니다.세 번째, 네 번째 및 다섯 번째 인수에 대한 매개 변수를 전달하지 마십시오. 매개 변수는 내부적으로 사용됩니다.

const loop = (arr, fn, busy, err, i=0) => {
  const body = (ok,er) => {
    try {const r = fn(arr[i], i, arr); r && r.then ? r.then(ok).catch(er) : ok(r)}
    catch(e) {er(e)}
  }
  const next = (ok,er) => () => loop(arr, fn, ok, er, ++i)
  const run  = (ok,er) => i < arr.length ? new Promise(body).then(next(ok,er)).catch(er) : ok()
  return busy ? run(busy,err) : new Promise(run)
}

const items = ['one', 'two', 'three']

loop(items, item => {
  console.info(item)
})
.then(() => console.info('Done!'))

고급 사용 사례

핸들러 함수, 중첩 루프 및 오류 처리에 대해 살펴보겠습니다.

핸들러(현재, 인덱스, 모두)

처리기는 3개의 인수를 전달받습니다.현재 항목, 현재 항목의 인덱스 및 전체 배열이 루프됩니다.처리기 함수가 비동기 작업을 수행해야 하는 경우, 약속을 반환할 수 있으며 루프 함수는 다음 반복을 시작하기 전에 약속이 해결될 때까지 기다립니다.루프 호출을 중첩하고 모든 작업을 예상대로 수행할 수 있습니다.

const loop = (arr, fn, busy, err, i=0) => {
  const body = (ok,er) => {
    try {const r = fn(arr[i], i, arr); r && r.then ? r.then(ok).catch(er) : ok(r)}
    catch(e) {er(e)}
  }
  const next = (ok,er) => () => loop(arr, fn, ok, er, ++i)
  const run  = (ok,er) => i < arr.length ? new Promise(body).then(next(ok,er)).catch(er) : ok()
  return busy ? run(busy,err) : new Promise(run)
}

const tests = [
  [],
  ['one', 'two'],
  ['A', 'B', 'C']
]

loop(tests, (test, idx, all) => new Promise((testNext, testFailed) => {
  console.info('Performing test ' + idx)
  return loop(test, (testCase) => {
    console.info(testCase)
  })
  .then(testNext)
  .catch(testFailed)
}))
.then(() => console.info('All tests done'))

오류 처리

제가 살펴본 많은 약속-루프 사례들은 예외가 발생할 때 깨집니다.이 기능을 사용하여 올바른 작업을 수행하는 것은 꽤 까다로웠지만, 제가 볼 때는 지금 작동하고 있습니다.내부 루프에 캐치 핸들러를 추가하고 발생 시 거부 기능을 호출해야 합니다.예:

const loop = (arr, fn, busy, err, i=0) => {
  const body = (ok,er) => {
    try {const r = fn(arr[i], i, arr); r && r.then ? r.then(ok).catch(er) : ok(r)}
    catch(e) {er(e)}
  }
  const next = (ok,er) => () => loop(arr, fn, ok, er, ++i)
  const run  = (ok,er) => i < arr.length ? new Promise(body).then(next(ok,er)).catch(er) : ok()
  return busy ? run(busy,err) : new Promise(run)
}

const tests = [
  [],
  ['one', 'two'],
  ['A', 'B', 'C']
]

loop(tests, (test, idx, all) => new Promise((testNext, testFailed) => {
  console.info('Performing test ' + idx)
  loop(test, (testCase) => {
    if (idx == 2) throw new Error()
    console.info(testCase)
  })
  .then(testNext)
  .catch(testFailed)  //  <--- DON'T FORGET!!
}))
.then(() => console.error('Oops, test should have failed'))
.catch(e => console.info('Succesfully caught error: ', e))
.then(() => console.info('All tests done'))

업데이트: NPM 패키지

이 답변을 작성한 이후, 저는 위의 코드를 NPM 패키지에 넣었습니다.

대규모의

설치하다

npm install --save for-async

수입품

var forAsync = require('for-async');  // Common JS, or
import forAsync from 'for-async';

사용량(비동기)

var arr = ['some', 'cool', 'array'];
forAsync(arr, function(item, idx){
  return new Promise(function(resolve){
    setTimeout(function(){
      console.info(item, idx);
      // Logs 3 lines: `some 0`, `cool 1`, `array 2`
      resolve(); // <-- signals that this iteration is complete
    }, 25); // delay 25 ms to make async
  })
})

자세한 내용은 패키지 읽기를 참조하십시오.

ES6로 제한된 경우에는 모두 약속하기가 가장 좋습니다.Promise.all(array)또한 의 모든 약속을 성공적으로 실행한 후 약속 배열을 반환합니다.arrayPromise의 데이터베이스의 많은 학생 기록을 업데이트하려는 경우 다음 코드가 약속의 개념을 보여준다고 가정합니다.이런 경우에는 모두

let promises = students.map((student, index) => {
    //where students is a db object
    student.rollNo = index + 1;
    student.city = 'City Name';
    //Update whatever information on student you want
    return student.save();
});
Promise.all(promises).then(() => {
    //All the save queries will be executed when .then is executed
    //You can do further operations here after as all update operations are completed now
});

지도는 루프의 예시적인 방법입니다.사용할 수도 있습니다.for또는forin또는forEach루프. 개념은 매우 간단합니다. 대량 비동기 작업을 수행할 루프를 시작하십시오.해당 루프의 범위 밖에 선언된 배열에서 해당 비동기 작업 문을 모두 푸시합니다.루프가 완료되면 이러한 쿼리/약속의 준비된 배열을 인수로 사용하여 Promise all 문을 실행합니다.

기본적인 개념은 자바스크립트 루프는 동기식인 반면 데이터베이스 호출은 비동기식이고 우리는 동기식인 루프에서 푸시 방식을 사용합니다.따라서 비동기 동작의 문제는 루프 내부에서 발생하지 않습니다.

여기 내 2센트 가치가 있습니다.

  • 한 기능forpromise()
  • 루프에 대한 고전을 에뮬레이트합니다.
  • 내부 로직을 기반으로 조기 종료가 가능하며, 값을 반환합니다.
  • 해결/다음/수집에 전달된 일련의 결과를 수집할 수 있습니다.
  • 기본값은 start=0,sys=1입니다.
  • 루프 내부에 던져진 예외가 포착되어 에 전달됩니다.

    function forpromise(lo, hi, st, res, fn) {
        if (typeof res === 'function') {
            fn = res;
            res = undefined;
        }
        if (typeof hi === 'function') {
            fn = hi;
            hi = lo;
            lo = 0;
            st = 1;
        }
        if (typeof st === 'function') {
            fn = st;
            st = 1;
        }
        return new Promise(function(resolve, reject) {

            (function loop(i) {
                if (i >= hi) return resolve(res);
                const promise = new Promise(function(nxt, brk) {
                    try {
                        fn(i, nxt, brk);
                    } catch (ouch) {
                        return reject(ouch);
                    }
                });
                promise.
                catch (function(brkres) {
                    hi = lo - st;
                    resolve(brkres)
                }).then(function(el) {
                    if (res) res.push(el);
                    loop(i + st)
                });
            })(lo);

        });
    }


    //no result returned, just loop from 0 thru 9
    forpromise(0, 10, function(i, next) {
        console.log("iterating:", i);
        next();
    }).then(function() {


        console.log("test result 1", arguments);

        //shortform:no result returned, just loop from 0 thru 4
        forpromise(5, function(i, next) {
            console.log("counting:", i);
            next();
        }).then(function() {

            console.log("test result 2", arguments);



            //collect result array, even numbers only
            forpromise(0, 10, 2, [], function(i, collect) {
                console.log("adding item:", i);
                collect("result-" + i);
            }).then(function() {

                console.log("test result 3", arguments);

                //collect results, even numbers, break loop early with different result
                forpromise(0, 10, 2, [], function(i, collect, break_) {
                    console.log("adding item:", i);
                    if (i === 8) return break_("ending early");
                    collect("result-" + i);
                }).then(function() {

                    console.log("test result 4", arguments);

                    // collect results, but break loop on exception thrown, which we catch
                    forpromise(0, 10, 2, [], function(i, collect, break_) {
                        console.log("adding item:", i);
                        if (i === 4) throw new Error("failure inside loop");
                        collect("result-" + i);
                    }).then(function() {

                        console.log("test result 5", arguments);

                    }).
                    catch (function(err) {

                        console.log("caught in test 5:[Error ", err.message, "]");

                    });

                });

            });


        });



    });

ES6에서 '대기용'을 사용해야 합니다.

(async () => {
  for await (const num of asyncIterable) {
    console.log(num);
  }
  // My action here
})();

자세한 내용은 ...대기를 참조하십시오.

저는 기출문제를 보고 혼란을 느낍니다.그리고 저는 답의 영감으로 다음을 코드화했습니다.나는 그것의 논리가 더 분명하다고 생각합니다. 나는 루프를 위해 원본을 대체하는 함수를 호출합니다.

async function pointToCountry(world, data) { // Data is for loop array
  if (data.length > 0) { // For condition
    const da = data.shift(); // Get current data and modified data one row code
    // Some business logic
    msg = da.info
    pointofView(world, da);
    // Await the current task
    await new Promise(r => setTimeout(_ => {
      r() // Resolve and finish the current task
    }, 5000))
    // Call itself and enter the next loop
    pointToCountry(world, data)
  } else { // Business logic after all tasks
    pointofView(world, { longitude: 0, latitude: 0 });
    world.controls().autoRotate = true;
  }
}
    // This is my main function - calculate all project by city
       const projectCity = async (req, res, next) => {
            try {         
                let record = [];            
                let cityList = await Cityodel.find({active:true});
            
                    for (let j = 0; j < cityList.length; j++) {
        
                    let arr = [];
                
                    let projectList   = await getProduct(cityList[j]._id)
        
                    arr.push({
                        _id:cityList[j]._id,
                        name:cityList[j].name,
                        projectList:projectList
                    })
          
                    record.push(arr);                
                }
        
                return res.status(200).send({ 
                    status: CONSTANT.REQUESTED_CODES.SUCCESS,
                    result: record });
            } catch (error) {
                return res.status(400).json(UTILS.errorHandler(error));
            }
        };
            
        
        async function getProduct(city){       
            let projectList = await ProjectModel.find({city:city});      
            return projectList;        
        }

재귀를 사용한 작업 시도는 다음과 같습니다.

function promisesWithRecursion() {
    const urlArray = [myUrl1, myUrl2, myUrl3]; //... and many more
    let i = 0;
    let call = ( () => fetch(urlArray[i], { method: "GET"})
    .then(response => response.text())
    .then(text => {
        //onSuccess()
        //console.log("response: " + text);
        document.getElementById("output").innerHTML += text;
        if(text.includes("ERROR")) {
            console.log("Error in url index " + i);
            console.log(urlArray[i]);
            return null;
        } else {
            nextUrl = urlArray[++i] ? urlArray[i] : null;
            if(!nextUrl) {
                return null;
            } else {
                return call();
            }
        }
    }
    )
    .catch(console.error.bind(console))
    );
    call();
}

이런 식으로 약속이 차례로 실행되고 오류가 발생하면 통화가 차단됩니다. 저는 다음과 같은 루프도 시도했습니다.

let prom = Promise.resolve();
for (const url of urlArray) {
    prom.then(() => fetch(url, { method: "GET"})
    .then(response => response.text())
    .then(text => {
        document.getElementById("output").innerHTML += text;
        if(text.includes("ERROR")) {
            break;
        }
    }).catch(...) );
}

또는

for (let i = 0; i < urlArray.length; i++) ...

하지만 제대로 작동할 수는 없어요. 모두 동시에 발사되고 있어요.

저는 Angular에서 약속 함수를 무한 반복하는 스니펫을 만들었습니다.시작, 중지 또는 재시작할 수 있습니다.

기본적으로 동일한 메소드를 재귀적으로 호출하고 현재 프로세스를 다음과 같이 기다려야 합니다.

async autoloop(): Promise<void> {
  if(this.running){
    await this.runMe();
    await this.autoloop();
  }
  return Promise.resolve();
}

JavaScript:

import { Component } from '@angular/core';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent {
  messages: string[] = [];
  counter = 1;
  running = false;

  constructor() {
    this.start();
  }

  onClick(): void {
    this.running = !this.running;
    if(this.running){
      this.start();
    }
    else{
      this.stop();
    }
  }

  async onRestartClick(): Promise<void>{
    await this.stop();
    this.messages = [];
    this.counter = 1;
    this.start();
  }

  start(): void{
    this.running = true;
    this.autoloop();
  }
  
  async stop(): Promise<void>{
    this.running = false;
    await this.delay(1000);
  }

  async autoloop(): Promise<void> {
    if(this.running){
      await this.runMe();
      await this.autoloop();
    }
    return Promise.resolve();
  }

  async runMe(): Promise<void> {
    await this.delay(1000);
    if(this.running){
      this.messages.push(`Message ${this.counter++}`);
    }
    return Promise.resolve();
  }

  async delay(ms: number) {
    await new Promise<void>((resolve) => setTimeout(() => resolve(), ms));
  }
}

HTML:

<h1>Endless looping a promise every 1 second</h1>
<button (click)="onClick()">Start / stop</button>
<button (click)="onRestartClick()">Restart</button>
<p *ngFor="let message of messages">
  {{message}}
</p>

언급URL : https://stackoverflow.com/questions/40328932/javascript-es6-promise-for-loop

반응형