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

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

陈政宽~
陈政宽~원래의
2017-06-28 14:39:291577검색

이 글은 주로 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() });


이런 식으로 함수에 이름이 있는지 여부는 중요하지 않습니다. 익명 함수를 직접 전달할 수도 있습니다. 테스트해 보겠습니다.


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') } 함수는 즉시 실행되지 않습니다( 시간 초과를 0으로 설정하더라도 fn2는 호출 후 즉시 반환된 다음 fn3을 실행합니다. 실제로는 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]를 사용하여 next가 호출될 때마다 인덱스가 1씩 증가하므로 가져오기 목적을 달성합니다. 다음 기능.
next는 다음과 같이 사용됩니다.


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


이제 stack.forEach 라인이 삭제되었으므로 Next를 직접 호출하여 실행할 스택에서 첫 번째 함수 fn1을 찾고 next를 호출합니다. fn1에서 다음 함수 fn2를 찾아 실행한 다음 fn2에서 다음 함수를 호출하는 식으로 진행됩니다.
모든 함수는 다음을 호출해야 합니다. 특정 함수에 작성되지 않은 경우 프로그램은 계속할 메커니즘 없이 함수 실행 후 바로 종료됩니다.

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


// 实现一个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 유명한 connectframework는 이런 방식으로 구현됩니다중간 부분 큐. 관심이 있으시면 해당 소스 코드나 "connect 미들웨어란 무엇입니까?"에 대한 설명을 살펴보세요.

조심하면 다음이 당분간 함수의 끝 부분에만 배치될 수 있다는 것을 알 수 있습니다. 중간에 배치해도 원래 문제는 여전히 나타납니다.


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


redux와 koa는 구현 방식이 다르므로 함수 중간에 next를 배치한 다음 후속 함수를 실행한 후 뒤로 돌아가서 아래 코드를 실행하는 것이 매우 영리합니다. 시간 나면 다시 쓰세요.

위 내용은 이 글의 전체 내용입니다. 여러분의 학습에 도움이 되기를 바랍니다.

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

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