Javascript에서 비동기 함수를 작성하는 방법


143

코드를 확인하십시오 :

<a href="#" id="link">Link</a>
<span>Moving</span>

$('#link').click(function () {
    console.log("Enter");
    $('#link').animate({ width: 200 }, 2000, function() {
         console.log("finished");            
    });    
    console.log("Exit");    
});

콘솔에서 볼 수 있듯이 "애니메이션"기능은 비동기식이며 이벤트 핸들러 블록 코드의 흐름을 "포크"합니다. 사실로 :

$('#link').click(function () {
    console.log("Enter");
    asyncFunct();
    console.log("Exit");    
});

function asyncFunct() {
    console.log("finished");
}

블록 코드의 흐름을 따르십시오!

function asyncFunct() { }이 동작으로 내 것을 만들려면 javascript / jquery로 어떻게 할 수 있습니까? 나는 사용하지 않고 전략이 있다고 생각 setTimeout()


jQuery 소스를 살펴보십시오 :)
yatskevich

.animate () mathod는 콜백을 사용합니다. 애니메이션이 완료되면 Animate에서 콜백을 호출합니다. .animate ()와 동일한 동작이 필요한 경우 콜백 (다른 작업 후 "main"함수에 의해 호출 됨)이 필요합니다. "완전한"비동기 기능 (실행 흐름을 차단하지 않고 호출 된 기능)이 필요한 경우 다릅니다. 이 경우 지연 시간이 거의 0 인 setTimeout ()을 사용할 수 있습니다.
Fabio Buda

@ Fabio Buda : 왜 callback ()이 일종의 비동기를 구현해야합니까? 실제로, 그것은 jsfiddle.net/5H9XT/9를
markzzz

사실 "콜백"후에는 setTimeout과 함께 "전체"비동기 메소드를 인용했습니다. 함수가 다른 코드 다음에 호출되는 방식으로 의사 비동기로 콜백을 의미했습니다 :-)
Fabio Buda

답변:


185

진정한 사용자 정의 비동기 기능을 만들 수 없습니다. 결국 다음과 같이 기본적으로 제공되는 기술을 활용해야합니다.

  • setInterval
  • setTimeout
  • requestAnimationFrame
  • XMLHttpRequest
  • WebSocket
  • Worker
  • File API, Web Database API와 같은 일부 HTML5 API
  • 지원하는 기술 onload
  • ... 많은 다른 사람

실제로 jQuery 애니메이션에을 사용합니다setInterval .


1
나는 어제 친구와 이것을 토론하고 있었 으므로이 답변은 완벽합니다! 비동기 함수를 이해하고 식별하여 JS에서 올바르게 사용할 수 있습니다. 그러나 우리가 맞춤형을 구현할 수 없는지 는 분명하지 않습니다. 블랙 박스와 같은 방식으로 작동하는 방식을 알고 setInterval있지만 (사용, 말하기 등 ) 어떻게 수행되는지 확인할 수는 없습니다. 주제에 대한 자세한 정보가 있습니까?
Matheus Felipe

2
@MatheusFelipe이 함수는 자바 스크립트 엔진 구현에 고유 한 것으로, HTML5 타이머같은 사양뿐 아니라 사양 에 따라 동작하는 블랙 박스 특성을 신뢰할 수 있습니다.
Spoike

10
@MatheusFelipe youtu.be/8aGhZQkoFbQ 이 주제와 관련하여 지금까지 가장 좋은 이야기 ...
Andreas Niedermair

일부 구현, 특히 Node.js는 다음을 지원합니다.setImmediate
Jon Surrell

어때 promises? 그것을 제공합니까 awaitable?
Nithin Chandran

69

타이머를 사용할 수 있습니다 :

setTimeout( yourFn, 0 );

(여기서 yourFn함수에 대한 참조가 있습니다)

또는 Lodash 와 함께 :

_.defer( yourFn );

func현재 호출 스택이 지워질 때까지 호출을 연기합니다 . 추가 인수는 func호출 될 때 제공 됩니다.


3
이것은 작동하지 않습니다. 캔버스에 그리는 내 자바 스크립트 함수는 UI가 응답하지 않게합니다.
gab06

2
@ gab06-캔버스 그리기 기능이 좋은 이유 때문에 차단되고 있다고 말하고 싶습니다. 동작을 여러 개의 작은 것으로 나누고 타이머를 사용하여 각각을 호출하십시오.이 방법으로 인터페이스가 마우스 클릭 등에 응답한다는 것을 알 수 있습니다.
Marco Faustinelli

링크가 끊어졌습니다.
JulianSoto

1
@JulianSoto 고정
Šime Vidas

1
setTimeoutHTML5 사양에 따라 최소 시간 은 4 밀리 초입니다. 0을 주면 여전히 최소 시간이 걸립니다. 그러나 예, 함수 지연기로 잘 작동합니다.
hegez 2016 년

30

여기에 간단한 해결책이 있습니다 (다른 것에 대해 쓰십시오) http://www.benlesh.com/2012/05/calling-javascript-function.html

그리고 여기 준비된 솔루션이 있습니다.

function async(your_function, callback) {
    setTimeout(function() {
        your_function();
        if (callback) {callback();}
    }, 0);
}

테스트 1 ( '1 x 2 3'또는 '1 2 x 3'또는 '1 2 3 x'출력 가능 ) :

console.log(1);
async(function() {console.log('x')}, null);
console.log(2);
console.log(3);

테스트 2 ( 항상 'x 1'출력 ) :

async(function() {console.log('x');}, function() {console.log(1);});

이 함수는 타임 아웃 0으로 실행됩니다. 비동기 작업을 시뮬레이션합니다


6
TEST 1은 실제로 '1 2 3 x'만 출력 할 수 있으며 TEST 2는 매번 '1 x'를 출력하도록 보장됩니다. TEST 2에서 예기치 않은 결과가 발생하는 이유는가 console.log(1)호출되고 출력 ( undefined)이 두 번째 인수로 전달 되기 때문 입니다 async(). TEST 1의 경우 JavaScript의 실행 대기열을 완전히 이해하지 못한다고 생각합니다. 각 호출 console.log()은 동일한 스택에서 발생 x하므로 마지막에 기록됩니다. 잘못된 정보에 대해서는이 답변에 투표를했지만 충분한 담당자가 없습니다.
Joshua Piccari

1
@Joshua : @fider는 TEST 2를 다음과 같이 쓰는 것으로 보입니다 : async(function() {console.log('x')}, function(){console.log(1)});.
nzn

@nzn 예와 @Joshua 나는 의미 TEST 2 as: async(function() {console.log('x')}, function(){console.log(1)});- 나는 이미 수정
FIDER

테스트 2 출력은 async (function () {setTimeout (() => {console.log ( 'x');}, 1000)}, function () {console.log (1);})에서 1 x입니다.
Mohsen

10

다음은 다른 기능을 수행하고 비동기를 실행하는 버전을 출력하는 기능입니다.

var async = function (func) {
  return function () {
    var args = arguments;
    setTimeout(function () {
      func.apply(this, args);
    }, 0);
  };
};

비동기 함수를 만드는 간단한 방법으로 사용됩니다.

var anyncFunction = async(function (callback) {
    doSomething();
    callback();
});

함수 자체에는 자체 구조 (콜백이 추가되지 않고 이미 함수에 있음)가 있고 사용할 수있는 새로운 함수를 생성하기 때문에 @fider의 대답과 다릅니다.


setTimeout은 루프에서 사용할 수 없습니다 (고유 한 인수로 동일한 함수를 여러 번 호출) .
user2284570

@ user2284570 이것이 폐쇄의 대상입니다. (function(a){ asyncFunction(a); })(a)
스위블

1
IIRC, 폐쇄없이 이것을 달성 할 수도 있습니다 :setTimeout(asyncFunction, 0, a);
Swivel

1
비동기 적으로 우리가 의미하는 경우 : 메인 스레드와 평행 한 백그라운드에서 실행하면 이것이 실제로 비동기가 아닙니다. 이 작업은 process.nextTick에 대한 실행을 지연시키는 것입니다. 함수에있는 코드가 무엇이든 메인 스레드에서 실행됩니다. 함수가 PI를 계산하도록 설정된 경우 시간 제한 유무에 관계없이 앱이 정지됩니다!
Mike M

1
이 답변이 왜 찬성인지 이해가되지 않습니다. 나는 내 코드에 넣고하면 함수까지 프로그램 블록은해야 정확히 어떤 완료 하지 않습니다.
Martin Argerami

6

편집 : 나는 질문을 완전히 오해했습니다. 브라우저에서을 사용 setTimeout합니다. 다른 스레드에서 실행하는 것이 중요하다면 Web Workers를 사용 합니다.


1
? 이것은 비동기 기능을하지 않습니다 : O
markzzz

6

늦었지만 ES6promises 도입 후 사용하는 쉬운 솔루션을 보여주기 위해 비동기 호출을 훨씬 쉽게 처리합니다.

새로운 약속에서 비동기 코드를 설정합니다.

var asyncFunct = new Promise(function(resolve, reject) {
    $('#link').animate({ width: 200 }, 2000, function() {
        console.log("finished");                
        resolve();
    });             
});

resolve()비동기 호출이 완료 될 때 설정해야합니다 .
그런 다음 .then()약속 내 에서 비동기 호출이 완료된 후 실행할 코드를 추가하십시오 .

asyncFunct.then((result) => {
    console.log("Exit");    
});

다음은 그 스 니펫입니다.

$('#link').click(function () {
    console.log("Enter");
    var asyncFunct = new Promise(function(resolve, reject) {
        $('#link').animate({ width: 200 }, 2000, function() {
            console.log("finished");            	
            resolve();
        }); 			
    });
    asyncFunct.then((result) => {
        console.log("Exit");    
    });
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<a href="#" id="link">Link</a>
<span>Moving</span>

또는 JSFiddle


6

이 페이지 는 비동기 자바 스크립트 함수 생성의 기본 사항을 안내합니다.

ES2017부터 비동기 javacript 함수를 작성하는 것이 훨씬 쉽습니다. 또한 Promises 에 대한 자세한 내용을 읽어보십시오 .


연결이 끊어졌습니다.
Martin Argerami

3

매개 변수를 사용하고 최대 비동기 함수 수를 조정하려면 간단한 비동기 작업자를 사용할 수 있습니다.

var BackgroundWorker = function(maxTasks) {
    this.maxTasks = maxTasks || 100;
    this.runningTasks = 0;
    this.taskQueue = [];
};

/* runs an async task */
BackgroundWorker.prototype.runTask = function(task, delay, params) {
    var self = this;
    if(self.runningTasks >= self.maxTasks) {
        self.taskQueue.push({ task: task, delay: delay, params: params});
    } else {
        self.runningTasks += 1;
        var runnable = function(params) {
            try {
                task(params);
            } catch(err) {
                console.log(err);
            }
            self.taskCompleted();
        }
        // this approach uses current standards:
        setTimeout(runnable, delay, params);
    }
}

BackgroundWorker.prototype.taskCompleted = function() {
    this.runningTasks -= 1;

    // are any tasks waiting in queue?
    if(this.taskQueue.length > 0) {
        // it seems so! let's run it x)
        var taskInfo = this.taskQueue.splice(0, 1)[0];
        this.runTask(taskInfo.task, taskInfo.delay, taskInfo.params);
    }
}

다음과 같이 사용할 수 있습니다.

var myFunction = function() {
 ...
}
var myFunctionB = function() {
 ...
}
var myParams = { name: "John" };

var bgworker = new BackgroundWorker();
bgworker.runTask(myFunction, 0, myParams);
bgworker.runTask(myFunctionB, 0, null);

2
Function.prototype.applyAsync = function(params, cb){
      var function_context = this;
      setTimeout(function(){
          var val = function_context.apply(undefined, params); 
          if(cb) cb(val);
      }, 0);
}

// usage
var double = function(n){return 2*n;};
var display = function(){console.log(arguments); return undefined;};
double.applyAsync([3], display);

다른 솔루션과 근본적으로 다르지는 않지만 내 솔루션이 몇 가지 추가 좋은 일을한다고 생각합니다.

  • 함수에 매개 변수를 허용합니다.
  • 함수의 출력을 콜백에 전달합니다.
  • Function.prototype더 좋은 방법으로 호출 할 수 있도록 추가되었습니다.

또한 내장 함수와의 유사성 Function.prototype.apply이 나에게 적합합니다.


1

@pimvdb의 훌륭한 답변 옆에 async.js 가 정말로 비동기 기능을 제공하지 않는 경우가 있습니다. 라이브러리의 주요 메소드의 (매우) 제거 된 버전은 다음과 같습니다.

function asyncify(func) { // signature: func(array)
    return function (array, callback) {
        var result;
        try {
            result = func.apply(this, array);
        } catch (e) {
            return callback(e);
        }
        /* code ommited in case func returns a promise */
        callback(null, result);
    };
}

따라서 함수는 오류를 방지하고 콜백으로 처리하여 처리하지만 코드는 다른 JS 함수와 동일합니다.


1

불행히도 JavaScript는 비동기 기능을 제공하지 않습니다. 단일 스레드에서만 작동합니다. 그러나 대부분의 최신 브라우저는 Workers를 제공합니다. 이는 백그라운드에서 실행되어 결과를 반환 할 수있는 두 번째 스크립트입니다. 그래서 나는 비동기식으로 함수를 실행하여 각 비동기 호출에 대한 작업자를 만드는 것이 유용하다고 생각하는 솔루션에 도달했습니다.

아래 코드에는 백그라운드에서 호출 하는 함수 포함되어 있습니다 async .

Function.prototype.async = function(callback) {
    let blob = new Blob([ "self.addEventListener('message', function(e) { self.postMessage({ result: (" + this + ").apply(null, e.data) }); }, false);" ], { type: "text/javascript" });
    let worker = new Worker(window.URL.createObjectURL(blob));
    worker.addEventListener("message", function(e) {
        this(e.data.result);
    }.bind(callback), false);
    return function() {
        this.postMessage(Array.from(arguments));
    }.bind(worker);
};

다음은 사용법의 예입니다.

(function(x) {
    for (let i = 0; i < 999999999; i++) {}
        return x * 2;
}).async(function(result) {
    alert(result);
})(10);

이 함수 for는 비동기 성을 나타내는데 시간을 내기 위해 엄청난 수의 a를 반복 한 다음 전달 된 수의 두 배를 얻는 함수를 실행합니다 . 이 async메소드는 function백그라운드에서 원하는 함수를 호출하는 함수를 제공 하며 고유 한 매개 변수에서 async콜백의 매개 변수로 제공되는 함수를 제공 return합니다. 콜백 함수 alert에서 결과입니다.


0

MDN에는 "this"를 유지하는 setTimeout을 사용 하는 좋은 예가 있습니다.

다음과 같이

function doSomething() {
    // use 'this' to handle the selected element here
}

$(".someSelector").each(function() {
    setTimeout(doSomething.bind(this), 0);
});
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.