>  기사  >  웹 프론트엔드  >  JavaScript 대기열 기능 및 asynchronous_javascript 기술에 대한 자세한 설명

JavaScript 대기열 기능 및 asynchronous_javascript 기술에 대한 자세한 설명

微波
微波원래의
2017-06-28 13:21:311357검색

이 글은 주로 JavaScriptqueue 함수와 비동기 실행 관련 정보를 자세하게 소개하고 있습니다. 참고할만한 가치가 있으니 관심 있는 분들은 참고하시면 됩니다.

편집자 주: 다른 분들의 JavaScript 코드를 리뷰하다 보면 비슷한 큐를 본 적이 있습니다. 함수가 순서대로 호출되는지 확인하기 위한 것입니다. 이 기사를 읽은 후 비동기 실행 등에 사용할 수도 있다는 것을 알았습니다.

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

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

그러나 때로는 이러한 함수가 런타임 중에 하나씩 추가되고 언제 추가될지 알 수 없습니다. 어떤 함수가 호출되나요? 이때 배열을 미리 정의할 수 있습니다. 함수를 추가할 때 해당 함수를 해당 배열에 하나씩 꺼내서 순서대로 호출하세요. 이렇게 하면 함수에 이름이 있든 없든 상관없이 그냥 넣으면 됩니다.

익명함수

도 전달할 수 있습니다. 테스트해 보겠습니다.

var stack = [];
// 执行其他操作,定义fn1
stack.push(fn1);
// 执行其他操作,定义fn2、fn3
stack.push(fn2, fn3);
// 调用的时候
stack.forEach(function(fn) { fn() });
이 구현은 지금까지 잘 작동했지만 비동기 함수 호출이라는 한 가지 상황을 무시했습니다. 비동기는 JavaScript에서 피할 수 없는 주제입니다. 여기서는 JavaScript의 비동기와 관련된 다양한 용어와 개념을 논의하지 않을 것입니다. 독자들이 직접 확인해 보시기 바랍니다(예: 유명한 논평). 다음 코드가 1, 3, 2를 출력한다는 것을 알고 계시다면 계속 읽어주세요:

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

function fn2() {
  console.log('第二个调用');
}
stack.push(fn2, function() { console.log('第三个调用') });

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

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

console.log(1);

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

console.log(3);

문제는 명백합니다. fn2는 실제로 순서대로 호출되지만 setTimeout의 fn2Timeout() { console.log('second call') } 함수는 즉시 실행되지 않습니다(timeout이 0으로 설정된 경우에도 fn2가 즉시 반환됨). 호출한 다음 fn3을 실행합니다. fn3이 실행된 후에는 실제로 fn2Timeout의 차례입니다.


어떻게 해결하나요? 분석해 보겠습니다. 여기서 핵심은 fn2Timeout입니다. fn3을 호출하기 전에 실제로 실행될 때까지 기다려야 합니다.

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() }); // 输出'第一个调用'、'第三个调用'、'第二个调用'

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

한 걸음 물러서서 fn3을 실행하기 전에 fn2Timeout이 완전히 실행될 때까지 기다리지 않고 대신 fn2Timeout 함수 본문의 마지막 줄에서 호출합니다.

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

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

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

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

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

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

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

next는 stack[index]를 사용하여 함수를 스택에 가져옵니다. next가 호출될 때마다 다음 함수를 가져오는 목적을 달성하기 위해 인덱스가 1씩 증가합니다.

Next는 다음과 같이 사용됩니다:

var index = 0;

function next() {
  var fn = stack[index];
  index = index + 1; // 其实也可以用shift 把fn 拿出来
  if (typeof fn === 'function') fn();
}

이제 stack.forEach 라인이 삭제되었으므로 우리는 직접 next를 호출합니다. Next는 실행할 스택에서 첫 번째 함수 fn1을 찾고 fn1에서 next를 호출하여 다음 함수를 찾습니다. fn2를 실행한 후 fn2에서 다음을 호출하는 식으로 진행됩니다.

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


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

var stack = [];

// 定义index 和next

function 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,最终按顺序输出'第一个调用'、'第二个调用'、'第三个调用'。

Node.js

이것이 유명한 connectframeworkmiddlewarequeue를 구현하는 방법입니다. 관심이 있으시면 해당 소스 코드나 "connect 미들웨어란 무엇입니까?"에 대한 설명을 살펴보세요.
조심해 보면 다음은 당분간 함수의 끝 부분에만 배치할 수 있다는 것을 알 수 있습니다. 중간에 배치해도 원래 문제는 여전히 발생합니다.

// 实现一个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
*/

// 以此类推。

redux 및 koa 다른 구현을 통해 함수에 다음 위치를 배치할 수 있습니다. 중간에 다음 함수를 실행한 후 다시 아래 코드를 실행하는데 이는 매우 영리합니다. 시간 나면 다시 쓰세요.

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

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