programing

JavaScript의 for 루프 내에서 비동기 함수 호출

projobs 2021. 1. 17. 10:21
반응형

JavaScript의 for 루프 내에서 비동기 함수 호출


다음 코드가 있습니다.

for(var i = 0; i < list.length; i++){
    mc_cli.get(list[i], function(err, response) {
        do_something(i);
    });
}

mc_climemcached 데이터베이스에 대한 연결입니다. 상상할 수 있듯이 콜백 함수는 비동기식이므로 for 루프가 이미 종료되었을 때 실행될 수 있습니다. 또한 이런 식으로 호출 할 때 do_something(i)항상 for 루프의 마지막 값을 사용합니다.

이런 식으로 클로저로 시도했습니다

do_something((function(x){return x})(i)) 

그러나 분명히 이것은 항상 for 루프 인덱스의 마지막 값을 사용하고 있습니다.

또한 for 루프 전에 함수를 다음과 같이 선언하려고 시도했습니다.

var create_closure = function(i) {
    return function() {
        return i;
    }
}

그리고 전화

do_something(create_closure(i)())

그러나 다시 성공하지 못했습니다. 반환 값은 항상 for 루프의 마지막 값입니다.

아무도 내가 폐쇄로 뭘 잘못하고 있는지 말해 줄 수 있습니까? 나는 그들을 이해한다고 생각했지만 이것이 작동하지 않는 이유를 알 수 없습니다.


배열을 통해 실행 중이므로 forEach목록 항목을 제공하는 항목과 콜백의 인덱스를 간단히 사용할 수 있습니다 . 반복에는 자체 범위가 있습니다.

list.forEach(function(listItem, index){
  mc_cli.get(listItem, function(err, response) {
    do_something(index);
  });
});

이것은 비동기 함수 내부 루프 패러다임이며 일반적으로 즉시 호출 된 익명 함수를 사용하여 처리합니다. 이렇게하면 인덱스 변수의 올바른 값으로 비동기 함수가 호출됩니다.

좋아요. 따라서 모든 비동기 함수가 시작되고 루프가 종료됩니다. 이제 이러한 함수가 언제 완료되는지, 비동기 적 특성으로 인해 또는 완료 될 순서를 알 수 없습니다. 실행하기 전에 이러한 모든 함수가 완료 될 때까지 기다려야하는 코드가있는 경우 완료된 함수 수를 간단히 계산하는 것이 좋습니다.

var total = parsed_result.list.length;
var count = 0;

for(var i = 0; i < total; i++){
    (function(foo){
        mc_cli.get(parsed_result.list[foo], function(err, response) {
            do_something(foo);
            count++;
            if (count > total - 1) done();
        });
    }(i));
}

// You can guarantee that this function will not be called until ALL of the
// asynchronous functions have completed.
function done() {
    console.log('All data has been loaded :).');
}

나는 이것이 오래된 스레드라는 것을 알고 있지만 어쨌든 내 대답을 추가합니다. ES2015 let에는 반복마다 루프 변수를 리 바인딩하는 기능이 있으므로 비동기 콜백에서 루프 변수의 값을 유지하므로 다음 중 하나를 시도해 볼 수 있습니다.

for(let i = 0; i < list.length; i++){
    mc_cli.get(list[i], function(err, response) {
        do_something(i);
    });
}

그러나 어쨌든 ES2015 기능이며 모든 브라우저 및 구현을 지원하지 않을 수 있으므로 forEach즉시 호출 된 기능을 사용하여 클로저 를 사용 하거나 만드는 let것이 좋습니다. 에서 여기 에서 Bindings ->let->for/for-in loop iteration scope나는 그것이까지 지원되지 않습니다 볼 수 있습니다 에지 (13) 심지어까지 파이어 폭스 49 (I이 브라우저에서 확인하지 않은 경우). 심지어 Node 4에서는 지원되지 않는다고 말하지만 개인적으로 테스트 한 결과 지원되는 것 같습니다.


꽤 가까웠지만 get콜백에 넣는 대신 클로저를 전달해야합니다 .

function createCallback(i) {
    return function(){
        do_something(i);
    }
}


for(var i = 0; i < list.length; i++){
    mc_cli.get(list[i], createCallback(i));
}

async/await구문을 사용하여 이것을 시도 하고Promise

(async function() {
    for(var i = 0; i < list.length; i++){
        await new Promise(next => {
            mc_cli.get(list[i], function(err, response) {
                do_something(i); next()
            })
        })
    }
})()

이렇게하면 next()기능이 트리거 될 때까지 각주기에서 루프가 중지됩니다.


ES2017 : 약속을 반환하는 함수 (예 : XHRPost) 내부에 비동기 코드를 래핑 할 수 있습니다 (약속 내부의 비동기 코드).

그런 다음 for 루프 내에서 마법의 Await 키워드를 사용하여 함수 (XHRPost)를 호출합니다. :)

let http = new XMLHttpRequest();
let url = 'http://sumersin/forum.social.json';

function XHRpost(i) {
    return new Promise(function(resolve) {
        let params = 'id=nobot&%3Aoperation=social%3AcreateForumPost&subject=Demo' + i + '&message=Here%20is%20the%20Demo&_charset_=UTF-8';
        http.open('POST', url, true);
        http.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
        http.onreadystatechange = function() {
                console.log("Done " + i + "<<<<>>>>>" + http.readyState);
                if(http.readyState == 4){
                    console.log('SUCCESS :',i);
                    resolve();
                }
        }
        http.send(params);       
   });
}

for (let i = 1; i < 5; i++) {
    await XHRpost(i);
   }

루프 내에서 비동기 함수를 실행하고 싶지만 콜백이 실행 된 후에도 인덱스 또는 기타 변수를 유지하려면 코드를 IIFE (즉시 호출 된 함수 표현식)로 래핑 할 수 있습니다.

var arr = ['Hello', 'World', 'Javascript', 'Async', ':)'];
for( var i = 0; i < arr.length; i++) {
  (function(index){
    setTimeout(function(){
       console.log(arr[index]);
 }, 500);

ES6 (typescript)를 사용하면 async의 이점을 사용할 수 있습니다 await.

let list: number[] = [1, 2, 3, 4, 5];

// this is async fucntion
function do_something(counter: number): Promise<number> {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log('called after ' + counter + ' seconds');
            resolve(counter);
        }, counter * 1000);
    })
}
async function foo() {
    // itrate over list and wait for when everything is finished
    let data = await Promise.all(list.map(async i => await do_something(i)));

    console.log(data);
}

foo();

ReferenceURL : https://stackoverflow.com/questions/13343340/calling-an-asynchronous-function-within-a-for-loop-in-javascript

반응형