>웹 프론트엔드 >JS 튜토리얼 >JavaScript 대기열 기능 및 비동기 실행에 대한 자세한 설명

JavaScript 대기열 기능 및 비동기 실행에 대한 자세한 설명

小云云
小云云원래의
2017-12-18 10:57:011997검색

다른 사람의 JavaScript 코드를 참조할 때 비슷한 대기열 함수를 본 적이 있는데, 이것이 함수가 순서대로 호출되는지 확인하기 위한 것임이 밝혀졌습니다. 이번 글에서는 주로 자바스크립트 큐 기능과 비동기 실행 방법을 소개합니다. 도움이 되길 바랍니다.

연속적으로 호출해야 하는 여러 함수 fn1, fn2 및 fn3이 있다고 가정해 보겠습니다. 물론 가장 간단한 방법은 다음과 같습니다.

fn1();fn2();fn3();

그러나 때로는 이러한 함수가 런타임 중에 하나씩 추가되는데, 그 함수가 무엇인지 알 수 없습니다. 어떤 함수를 사용하나요? 이때 함수를 미리 정의할 수 있습니다. 함수를 추가할 때 해당 함수를 배열에서 하나씩 꺼내서 순서대로 호출하세요.

var stack = [];// 执行其他操作,定义fn1stack.push(fn1);// 执行其他操作,定义fn2、fn3stack.push(fn2, fn3);// 调用的时候stack.forEach(function(fn) { fn() });

이렇게 하면 함수에 이름이 있든 없든 상관없이 그냥 넣으면 됩니다. 익명 함수도 전달할 수 있습니다. 테스트해 보겠습니다.

var stack = [];function fn1() {
    console.log('第一个调用');
}
stack.push(fn1);function fn2() {
    console.log('第二个调用');
}
stack.push(fn2, function() { console.log('第三个调用') });

stack.forEach(function(fn) { fn() }); // 按顺序输出'第一个调用'、'第二个调用'、'第三个调用'

이 구현은 지금까지 잘 작동했지만 비동기 함수 호출이라는 한 가지 상황을 무시했습니다. 비동기는 JavaScript에서 피할 수 없는 주제입니다. 여기서는 JavaScript의 비동기와 관련된 다양한 용어와 개념을 논의하지 않을 것입니다. 독자들이 직접 확인해 보시기 바랍니다(예: 유명한 논평). 다음 코드가 1, 3, 2를 출력한다는 것을 알고 계시다면 계속해서 읽어주세요:

console.log(1);

setTimeout(function() {
    console.log(2);
}, 0);console.log(3);

스택 큐에 비슷한 비동기 함수인 함수가 있으면 구현이 엉망이 될 것입니다:

var stack = [];function fn1() { console.log('第一个调用') };
stack.push(fn1);function fn2() {
    setTimeout(function fn2Timeout() {
         console.log('第二个调用');
    }, 0);
}
stack.push(fn2, function() { console.log('第三个调用') });

stack.forEach(function(fn) { fn() }); // 输出'第一个调用'、'第三个调用'、'第二个调用'

문제는 명백합니다. fn2는 실제로 순서대로 호출되지만 setTimeout의 fn2Timeout() { console.log('second call') } 함수는 즉시 실행되지 않습니다(timeout이 0으로 설정된 경우에도 fn2가 즉시 반환됨). 호출한 다음 fn3을 실행합니다. fn3이 실행된 후에는 실제로 fn2Timeout의 차례입니다.
어떻게 해결하나요? 분석해 보겠습니다. 여기서 핵심은 fn2Timeout입니다. fn3을 호출하기 전에 실제로 실행될 때까지 기다려야 합니다.

function fn2() {
    setTimeout(function() {
        fn2Timeout();
        fn3();
    }, 0);
}

그러나 그렇게 하는 것은 원래 fn2Timeout을 제거하고 새 함수로 바꾸는 것과 같습니다. 를 선택한 다음 원래 fn2Timeout 및 fn3을 삽입합니다. 원래 기능을 동적으로 변경하는 이 방법에는 Monkey Patch라는 특별한 용어가 있습니다. 우리 프로그래머들의 진언에 따르면: "확실히 이루어질 수 있다"이지만, 작성하기가 약간 어색하고 직접 참여하기가 쉽습니다. 더 좋은 방법이 있나요?
한 걸음 물러서서 fn3을 실행하기 전에 fn2Timeout이 완전히 실행될 때까지 기다리지 않고 대신 fn2Timeout 함수 본문의 마지막 줄에서 호출합니다.

function fn2() {
    setTimeout(function fn2Timeout() {
        console.log('第二个调用');
        fn3();       // 注{1}
    }, 0);
}

이것이 더 좋아 보이지만 fn2가 정의되면 fn3은 아직 없나요? fn3은 어디서 나온건가요?

또 다른 문제가 있습니다. fn2에서 fn3을 호출해야 하기 때문에 stack.forEach를 통해 fn3을 호출할 수 없습니다. 그렇지 않으면 fn3이 두 번 호출됩니다.

fn3을 fn2에 쓸 수 없습니다. 대신 fn2Timeout이 끝날 때 스택에서 fn2의 다음 함수를 찾은 다음 다음을 호출하기만 하면 됩니다.

function fn2() {
    setTimeout(function fn2Timeout() {
        console.log('第二个调用');        next();
    }, 0);
}

이 다음 함수는 스택에서 다음 함수를 찾아 실행하는 일을 담당합니다. 이제 next를 구현해 보겠습니다.

var index = 0;

function next() {
    var fn = stack[index];    index = index + 1; // 其实也可以用shift 把fn 拿出来    if (typeof fn === 'function') fn();
}next通过stack[index]去获取stack中的函数,每调用next一次index会加1,从而达到取出下一个函数的目的。

next는 다음과 같이 사용됩니다.

var stack = [];

// 定义index 和nextfunction fn1() {
    console.log('第一个调用');    next();  // stack 中每一个函数都必须调用`next`
};
stack.push(fn1);function fn2() {
    setTimeout(function fn2Timeout() {
         console.log('第二个调用');         next();  // 调用`next`
    }, 0);
}
stack.push(fn2, function() {
    console.log('第三个调用');    next(); // 最后一个可以不调用,调用也没用。
});next(); // 调用next,最终按顺序输出'第一个调用'、'第二个调用'、'第三个调用'。
现在stack.forEach一行已经删掉了,我们自行调用一次next,next会找出stack中的第一个函数fn1执行,fn1 里调用next,去找出下一个函数fn2并执行,fn2里再调用next,依此类推。

모든 함수는 next를 호출해야 합니다. 함수에 작성되지 않은 경우 프로그램은 계속할 메커니즘 없이 함수를 실행한 후 바로 종료됩니다.

함수 대기열의 구현을 이해한 후에는 다음 인터뷰 질문을 해결할 수 있어야 합니다.

// 实现一个LazyMan,可以按照以下方式调用:
LazyMan(“Hank”)/* 输出: 
Hi! This is Hank!
*/LazyMan(“Hank”).sleep(10).eat(“dinner”)输出/* 输出: 
Hi! This is Hank!
// 等待10秒..
Wake up after 10
Eat dinner~
*/LazyMan(“Hank”).eat(“dinner”).eat(“supper”)/* 输出: 
Hi This is Hank!
Eat dinner~
Eat supper~
*/LazyMan(“Hank”).sleepFirst(5).eat(“supper”)/* 等待5秒,输出
Wake up after 5
Hi This is Hank!
Eat supper
*/// 以此类推。
Node.js 中大名鼎鼎的connect框架正是这样实现中间件队列的。有兴趣可以去看看它的源码或者这篇解读《何为 connect 中间件》。

조심하면 이 다음은 당분간 함수의 끝에만 배치될 수 있다는 것을 알 수 있습니다. 중간에 배치하면 원래 문제도 발생합니다.

function fn() {
    console.log(1);
    next();
    console.log(2); // next()如果调用了异步函数,console.log(2)就会先执行}

redux와 koa는 다른 구현을 통해 next를 함수 중간에 배치하고 후속 함수를 실행한 후 다시 돌아가서 다음 코드를 실행할 수 있습니다. , 매우 영리합니다. 시간 나면 다시 쓰세요.

관련 추천:

이벤트 바인딩 문제를 해결하기 위해 캡슐화된 자바스크립트 이벤트 큐 함수 코드를 공유하세요

나만의 캡슐화된 자바스크립트 이벤트 큐 함수 버전_javascript 기술

jquery 큐 함수 사용법 example_jquery

위 내용은 JavaScript 대기열 기능 및 비동기 실행에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.