Programing

계속하기 전에 하나의 기능이 완료되기를 기다리는 적절한 방법은 무엇입니까?

lottogame 2020. 6. 24. 07:57
반응형

계속하기 전에 하나의 기능이 완료되기를 기다리는 적절한 방법은 무엇입니까?


두 개의 JS 함수가 있습니다. 하나는 다른 하나를 호출합니다. 호출 기능 내에서 다른 기능을 호출하고 해당 기능이 완료 될 때까지 기다렸다가 계속 진행하고 싶습니다. 예를 들어 / 의사 코드 :

function firstFunction(){
    for(i=0;i<x;i++){
        // do something
    }
};

function secondFunction(){
    firstFunction()
    // now wait for firstFunction to finish...
    // do something else
};

나는이 해결책을 생각해 냈지만 이것이 현명한 방법인지는 모른다.

var isPaused = false;

function firstFunction(){
    isPaused = true;
    for(i=0;i<x;i++){
        // do something
    }
    isPaused = false;
};

function secondFunction(){
    firstFunction()
    function waitForIt(){
        if (isPaused) {
            setTimeout(function(){waitForIt()},100);
        } else {
            // go do that thing
        };
    }
};

합법적입니까? 그것을 처리하는 더 우아한 방법이 있습니까? 아마도 jQuery와 함께?


이와 같은 비동기 작업을 처리하는 한 가지 방법은 다음과 같은 콜백 함수를 사용하는 것입니다.

function firstFunction(_callback){
    // do some asynchronous work
    // and when the asynchronous stuff is complete
    _callback();    
}

function secondFunction(){
    // call first function and pass in a callback function which
    // first function runs when it has completed
    firstFunction(function() {
        console.log('huzzah, I\'m done!');
    });    
}

@Janaka Pushpakumara의 제안에 따라 화살표 기능을 사용하여 동일한 것을 얻을 수 있습니다. 예를 들면 다음과 같습니다.

firstFunction(() => console.log('huzzah, I\'m done!'))


여기서 중요한 점이 누락 된 것 같습니다. JavaScript는 단일 스레드 실행 환경입니다. 코드를 다시 살펴보면 다음을 추가했습니다 alert("Here").

var isPaused = false;

function firstFunction(){
    isPaused = true;
    for(i=0;i<x;i++){
        // do something
    }
    isPaused = false;
};

function secondFunction(){
    firstFunction()

    alert("Here");

    function waitForIt(){
        if (isPaused) {
            setTimeout(function(){waitForIt()},100);
        } else {
            // go do that thing
        };
    }
};

You don't have to wait for isPaused. When you see the "Here" alert, isPaused will be false already, and firstFunction will have returned. That's because you cannot "yield" from inside the for loop (// do something), the loop may not be interrupted and will have to fully complete first (more details: Javascript thread-handling and race-conditions).

That said, you still can make the code flow inside firstFunction to be asynchronous and use either callback or promise to notify the caller. You'd have to give up upon for loop and simulate it with if instead (JSFiddle):

function firstFunction()
{
    var deferred = $.Deferred();

    var i = 0;
    var nextStep = function() {
        if (i<10) {
            // Do something
            printOutput("Step: " + i);
            i++;
            setTimeout(nextStep, 500); 
        }
        else {
            deferred.resolve(i);
        }
    }
    nextStep();
    return deferred.promise();
}

function secondFunction()
{
    var promise = firstFunction();
    promise.then(function(result) { 
        printOutput("Result: " + result);
    });
}

On a side note, JavaScript 1.7 has introduced yield keyword as a part of generators. That will allow to "punch" asynchronous holes in otherwise synchronous JavaScript code flow (more details and an example). However, the browser support for generators is currently limited to Firefox and Chrome, AFAIK.


Use async/await :

async function firstFunction(){
  for(i=0;i<x;i++){
    // do something
  }
  return;
};

then use await in your other function to wait for it to return:

async function secondFunction(){
  await firstFunction();
  // now wait for firstFunction to finish...
  // do something else
};

The only issue with promises is that IE doesn't support them. Edge does, but there's plenty of IE 10 and 11 out there: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise (compatibility at the bottom)

So, JavaScript is single-threaded. If you're not making an asynchronous call, it will behave predictably. The main JavaScript thread will execute one function completely before executing the next one, in the order they appear in the code. Guaranteeing order for synchronous functions is trivial - each function will execute completely in the order it was called.

Think of the synchronous function as an atomic unit of work. The main JavaScript thread will execute it fully, in the order the statements appear in the code.

But, throw in the asynchronous call, as in the following situation:

showLoadingDiv(); // function 1

makeAjaxCall(); // function 2 - contains async ajax call

hideLoadingDiv(); // function 3

This doesn't do what you want. It instantaneously executes function 1, function 2, and function 3. Loading div flashes and it's gone, while the ajax call is not nearly complete, even though makeAjaxCall() has returned. THE COMPLICATION is that makeAjaxCall() has broken its work up into chunks which are advanced little by little by each spin of the main JavaScript thread - it's behaving asychronously. But that same main thread, during one spin/run, executed the synchronous portions quickly and predictably.

So, the way I handled it: Like I said the function is the atomic unit of work. I combined the code of function 1 and 2 - I put the code of function 1 in function 2, before the asynch call. I got rid of function 1. Everything up to and including the asynchronous call executes predictably, in order.

THEN, when the asynchronous call completes, after several spins of the main JavaScript thread, have it call function 3. This guarantees the order. For example, with ajax, the onreadystatechange event handler is called multiple times. When it reports it's completed, then call the final function you want.

I agree it's messier. I like having code be symmetric, I like having functions do one thing (or close to it), and I don't like having the ajax call in any way be responsible for the display (creating a dependency on the caller). BUT, with an asynchronous call embedded in a synchronous function, compromises have to be made in order to guarantee order of execution. And I have to code for IE 10 so no promises.

Summary: For synchronous calls, guaranteeing order is trivial. Each function executes fully in the order it was called. For a function with an asynchronous call, the only way to guarantee order is to monitor when the async call completes, and call the third function when that state is detected.

For a discussion of JavaScript threads, see: https://medium.com/@francesco_rizzi/javascript-main-thread-dissected-43c85fce7e23 and https://developer.mozilla.org/en-US/docs/Web/JavaScript/EventLoop

Also, another similar, highly rated question on this subject: How should I call 3 functions in order to execute them one after the other?


An elegant way of waiting for one function to complete first is to use Promises with async/await function .

Quick example:

let firstFunction = new Promise(function(resolve, reject) {
    let x = 10
    let y = 0
    setTimeout(function(){
      for(i=0;i<x;i++){
         y++
      }
       console.log('loop completed')  
       resolve(y)
    }, 2000)
})

Firstly, create a Promise. The function above will be completed after 2s. I used setTimeout in order to demonstrate the situation where the instructions would take some time to execute (based on your example).

async function secondFunction(){
    let result = await firstFunction
    console.log(result)
    console.log('next step')
}; 

secondFunction()

For the second function, you can use async/await function where you will await for the first function to complete before proceeding with the instructions.

Note: You could simply resolve the Promise without any value like so resolve(). In my example, I resolved the Promise with the value of y that I can then use in the second function.

Hope it helps for people who still visit this thread.

참고URL : https://stackoverflow.com/questions/21518381/proper-way-to-wait-for-one-function-to-finish-before-continuing

반응형