JS 비동기 프로그래밍 소개

不言
풀어 주다: 2018-07-07 09:52:25
원래의
2504명이 탐색했습니다.

이 기사는 주로 참조 가치가 있는 JS 비동기 프로그래밍에 대한 소개를 소개합니다. 이제 모든 사람과 공유합니다. 도움이 필요한 친구들이 참조할 수 있습니다.

JS 비동기 프로그래밍 (1)

1.1 비동기란 무엇입니까

비동기(async)는 异步(async)是相对于同步(sync)而言的,很好理解。

同步就是一件事一件事的执行。只有前一个任务执行完毕,才能执行后一个任务。而异步比如:

setTimeout(function cbFn(){    console.log('learnInPro');
}, 1000);console.log('sync things');
로그인 후 복사

setTimeout就是一个异步任务,当JS引擎顺序执行到setTimeout的时候发现他是个异步任务,则会把这个任务挂起,继续执行后面的代码。直到1000ms后,回调函数cbFn才会执行,这就是异步,在执行到setTimeout的时候,JS并不会傻呵呵的等着1000ms执行cbFn回调函数,而是继续执行了后面的代码。

1.2 为啥要在JS中使用异步

由于javascript是单线程的,只能在JS引擎的主线程上运行的,所以js代码只能一行一行的执行,不能在同一时间执行多个js代码任务,这就导致如果有一段耗时较长的计算,或者是一个ajax请求等IO操作,如果没有异步的存在,就会出现用户长时间等待,并且由于当前任务还未完成,所以这时候所有的其他操作都会无响应。

1.3 那为啥JS不设计成多线程的

这主要跟javascript的历史有关,js最开始只是为了处理一些表单验证和DOM操作而被创造出来的,所以主要为了语言的轻量和简单采用了单线程的模式。多线程模型相比单线程

<코드 스타일="글꼴 크기: 0.85em; 글꼴 가족: Consolas, Inconsolata, Courier, 고정 폭; 여백: 0px 0.15em; 패딩: 0px 0.3em; 공백: 사전 포장; 테두리: 1px 솔리드 #eaeaea; background-color: #f8f8f8; display: inline">동기화는 한 가지의 실행입니다. 다음 작업은 이전 작업이 실행된 후에만 실행할 수 있습니다. 그리고 비동기예:

setTimeout(() => {    console.log(&#39;我是第一个宏任务&#39;);
    Promise.resolve().then(() => {        console.log(&#39;我是第一个宏任务里的第一个微任务&#39;);
    });
    Promise.resolve().then(() => {        console.log(&#39;我是第一个宏任务里的第二个微任务&#39;);
    });
}, 0);

setTimeout(() => {    console.log(&#39;我是第二个宏任务&#39;);
}, 0);

Promise.resolve().then(() => {    console.log(&#39;我是第一个微任务&#39;);
});console.log(&#39;执行同步任务&#39;);
로그인 후 복사
로그인 후 복사

setTimeout은 비동기 작업 JS 엔진 setTimeout을 순차적으로 실행하여 비동기 작업인 것으로 확인되면 작업을 일시 중지하고 후속 코드를 계속 실행합니다. 콜백 함수 cbFn은 1000ms 이후까지 실행되지 않습니다. 이는 비동기식입니다. setTimeout이 실행되면 JS는 cbFn 콜백 함수를 실행하기 위해 어리석게도 1000ms를 기다리지 않고 다음 코드를 계속 실행합니다. 🎜🎜🎜1.2 JS에서 비동기식을 사용하는 이유🎜🎜🎜javascript는 단일 스레드, 따라서 JS 엔진의 메인 스레드에서만 실행될 수 있습니다. js 코드는 한 줄씩만 실행할 수 있으며 여러 js 코드 작업을 동시에 실행할 수 없습니다. 즉, 긴 계산이 있거나 ajax 요청과 같은 IO 작업이 있는 경우 비동기가 없으면 사용자는 오랜 시간 동안 기다리게 되며 현재 작업이 완료되지 않았기 때문에 현재 다른 모든 작업은 응답하지 않습니다. 🎜🎜🎜1.3 그러면 JS는 왜 멀티 스레드로 설계되지 않았습니까?🎜🎜🎜이것은 주로 javascript의 역사와 관련이 있습니다. js는 원래 일부 양식 유효성 검사 및 DOM 작업을 처리하기 위해 만들어졌으므로 주로 경량화를 위한 것입니다. 단일 스레드 모드. 다중 스레드 모델 비교 단일 스레드 여러 스레드를 처리해야 합니다. 스레드 간 리소스 공유 문제도 상태 동기화 등의 해결이 필요합니다. 🎜🎜JS가 멀티스레드인 경우, p에 DOM을 삽입하는 작업을 수행하려고 하고, 다른 스레드가 p를 삭제하는 작업을 수행하는 경우 이때 많은 문제가 발생하게 되며 이에 대한 잠금도 추가해야 합니다. 이 메커니즘 등 🎜

좋아, 이제 우리는 단일 스레드 JS가 긴 대기 시간을 피하기 위해 비동기 처리를 사용한다는 것을 알았습니다. 예를 들어 ajax 작업을 수행할 때 js가 요청을 보내면 서버 데이터가 반환될 때까지 기다리지 않고 계속해서 후속 작업을 수행하고 js 엔진에 처리를 알리기 전에 서버 데이터가 반환될 때까지 기다립니다. 그것.

그럼 일반적인 비동기 패턴은 무엇인가요?

  • 콜백 기능

  • 이벤트 모니터링

  • 게시/구독 모드(관찰자 모드라고도 함)

  • promise

나중에 ES6에서는 생성기 함수, ES7에서는 async/await는 비동기 프로그래밍을 새로운 단계로 가져옵니다. Generator函数;ES7中,async/await更是将异步编程带入了一个全新的阶段。

这些异步模式我们会在后面详细来说,这里我们有个概念就好。


1.4 JS如何实现异步

具体JS是如何实现异步操作的呢?

答案就是JS的事件循环机制(Event Loop)

具体来说:

当JS解析执行时,会被引擎分为两类任务,同步任务(synchronous)异步任务(asynchronous)

对于同步任务来说,会被推到执行栈按顺序去执行这些任务。
对于异步任务来说,当其可以被执行时,会被放到一个 任务队列(task queue) 里等待JS引擎去执行。

当执行栈中的所有同步任务完成后,JS引擎才会去任务队列里查看是否有任务存在,并将任务放到执行栈中去执行,执行完了又会去任务队列里查看是否有已经可以执行的任务。这种循环检查的机制,就叫做事件循环(Event Loop)

이러한 비동기 모드에 대해서는 나중에 자세히 설명하겠지만 여기에는 개념이 있습니다. 🎜
🎜

1.4 비동기 JS 구현 방법

🎜 JS는 비동기 작업을 어떻게 구현하나요? 🎜🎜답은 JS 이벤트 루프 메커니즘(이벤트 루프). 🎜🎜<코드 스타일="글꼴 크기: 0.85em; 글꼴 가족: Consolas, Inconsolata, Courier, 고정 폭; 여백: 0px 0.15em; 패딩: 0px 0.3em; 공백: 사전 포장; 테두리: 1px 솔리드 #eaeaea; background-color: #f8f8f8; display: inline">구체적으로:🎜🎜JS가 구문 분석되고 실행되면 엔진에 의해 두 가지 유형의 작업으로 구분됩니다. 크기: 0.85 em; 글꼴 계열: Consolas, Inconsolata, 고정 폭: 0px 0.15em; 공백: pre-wrap; 테두리: 1px 배경색: #f8f8f8 ; 디스플레이: 인라인">동기 작업(동기) 및 비동기 작업. 🎜🎜동기식 작업의 경우 해당 작업을 순서대로 실행하기 위해 실행 스택으로 푸시됩니다.
비동기 작업의 경우 실행 가능하면 작업 대기열 JS 엔진 구현을 기다리고 있습니다. 🎜🎜실행 스택의 모든 동기화 작업이 완료되면 JS 엔진은 작업 큐로 이동하여 작업이 있는지 확인하고 실행을 위해 작업을 실행 스택에 넣습니다. 이미 수행할 수 있는 작업이 있는지 확인하기 위해 작업 대기열에 추가합니다. 이 루프 검사 메커니즘을 이벤트 루프. 🎜

对于任务队列,其实是有更细的分类。其被分为 微任务(microtask)队列 & 宏任务(macrotask)队列

宏任务: setTimeout、setInterval等,会被放在宏任务(macrotask)队列。

微任务: Promise的then、Mutation Observer等,会被放在微任务(microtask)队列。

Event Loop的执行顺序是:

  1. 首先执行执行栈里的任务。

  2. 执行栈清空后,检查微任务(microtask)队列,将可执行的微任务全部执行。

  3. 取宏任务(macrotask)队列中的第一项执行。

  4. 回到第二步。

注意: 微任务队列每次全执行,宏任务队列每次只取一项执行。

我们举个例子:

setTimeout(() => {    console.log(&#39;我是第一个宏任务&#39;);
    Promise.resolve().then(() => {        console.log(&#39;我是第一个宏任务里的第一个微任务&#39;);
    });
    Promise.resolve().then(() => {        console.log(&#39;我是第一个宏任务里的第二个微任务&#39;);
    });
}, 0);

setTimeout(() => {    console.log(&#39;我是第二个宏任务&#39;);
}, 0);

Promise.resolve().then(() => {    console.log(&#39;我是第一个微任务&#39;);
});console.log(&#39;执行同步任务&#39;);
로그인 후 복사
로그인 후 복사

最后的执行结果是:

  • // 执行同步任务

  • // 我是第一个微任务

  • // 我是第一个宏任务

  • // 我是第一个宏任务里的第一个微任务

  • // 我是第一个宏任务里的第二个微任务

  • // 我是第二个宏任务


1.5 JS异步编程模式

这里我们已经知道了JS中异步的运行机制,我们翻回头来详细的了解一下常见的各种异步的编程模式。

  • 回调函数

  • 事件监听

  • 发布/订阅模式

  • Promise

  • Generator

  • async/await

1.5.1 回调函数

回调函数是异步操作最基本的方法。

比如:我有一个异步操作(asyncFn),和一个同步操作(normalFn)。

function asyncFn() {
    setTimeout(() => {        console.log(&#39;asyncFn&#39;);
    }, 0)
}function normalFn() {    console.log(&#39;normalFn&#39;);
}

asyncFn();
normalFn();// normalFn// asyncFn
로그인 후 복사

如果按照正常的JS处理机制来说,同步操作一定发生在异步之前。如果我想要将顺序改变,最简单的方式就是使用回调的方式处理。

function asyncFn(callback) {
    setTimeout(() => {        console.log(&#39;asyncFn&#39;);
        callback();
    }, 0)
}function normalFn() {    console.log(&#39;normalFn&#39;);
}

asyncFn(normalFn);// asyncFn// normalFn
로그인 후 복사

1.5.2 事件监听

另一种思路是采用事件驱动模式。这种思路是说异步任务的执行不取决于代码的顺序,而取决于某个事件是否发生。

比如一个我们注册一个按钮的点击事件或者注册一个自定义事件,然后通过点击或者trigger的方式触发这个事件。

1.5.3 发布/订阅模式(又称观察者模式)

这个重点讲下,发布/订阅模式像是事件监听模式的升级版。

在发布/订阅模式中,你可以想象存在一个消息中心的地方,你可以在那里“注册一条消息”,那么被注册的这条消息可以被感兴趣的若干人“订阅”,一旦未来这条“消息被发布”,则所有订阅了这条消息的人都会得到提醒。

这个就是发布/订阅模式的设计思路。接下来我们一点一点实现一个简单的发布/订阅模式。

首先我们先实现一个消息中心。

// 先实现一个消息中心的构造函数,用来创建一个消息中心function MessageCenter(){    var _messages = {}; // 所有注册的消息都存在这里

    this.regist = function(){}; // 用来注册消息的方法
    this.subscribe = function(){};  // 用来订阅消息的方法
    this.fire = function(){};   // 用来发布消息的方法}
로그인 후 복사

这里一个消息中心的雏形就创建好了,接下来我们只要完善下regist,subscribe和fire这三个方法就好了。

function MessageCenter(){    var _messages = {};    // 对于regist方法,它只负责注册消息,就只接收一个注册消息的类型(标识)参数就好了。
    this.regist = function(msgType){        // 判断是否重复注册
        if(typeof _messages[msgType] === &#39;undefined&#39;){
            _messages[msgType] = [];    // 数组中会存放订阅者
        }else{            console.log(&#39;这个消息已经注册过了&#39;);
        }
    }    // 对于subscribe方法,需要订阅者和已经注册了的消息进行绑定
    // 由于订阅者得到消息后需要处理消息,所以他是一个个的函数
    this.subscribe = function(msgType, subFn){        // 判断是否有这个消息
        if(typeof _messages[msgType] !== &#39;undefined&#39;){
            _messages[msgType].push(subFn);
        }else{            console.log(&#39;这个消息还没注册过,无法订阅&#39;)
        }
    }    // 最后我们实现下fire这个方法,就是去发布某条消息,并通知订阅这条消息的所有订阅者函数
    this.fire = function(msgType, args){    
        // msgType是消息类型或者说是消息标识,而args可以设置这条消息的附加信息

        // 还是发布消息时,判断下有没有这条消息
        if(typeof _messages[msgType] === &#39;undefined&#39;) {            console.log(&#39;没有这条消息,无法发布&#39;);            return false;
        }        var events = {
            type: msgType,
            args: args || {}
        };

        _messages[msgType].forEach(function(sub){
            sub(events);
        })
    }
}
로그인 후 복사

这样,一个简单的发布/订阅模式就完成了,当然这只是这种模式的其中一种简单实现,还有很多其他的实现方式。
就此我们就可以用他来处理一些异步操作了。

var msgCenter = new MessageCenter();

msgCenter.regist(&#39;A&#39;);
msgCenter.subscribe(&#39;A&#39;, subscribeFn);function subscribeFn(events) {    console.log(events.type, events.args);
} 

// -----setTimeout(function(){
    msgCenter.fire(&#39;A&#39;, &#39;fire msg&#39;);
}, 1000);// A, fire msg
로그인 후 복사

 

我们在这篇文章里深入讲解了什么是异步,为什么要有异步以及在JS中引擎是如何处理异步的,后面我们讲解了几种异步编程模式并重点讲了下发布/订阅模式,
在下一章里面我们重点把另外几种异步编程模式Promise,Generator,async/await讲完。

以上就是本文的全部内容,希望对大家的学习有所帮助,更多相关内容请关注PHP中文网!

相关推荐:

JavaScript Error对象的解析

小程序中使用ECharts 异步加载数据


위 내용은 JS 비동기 프로그래밍 소개의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
원천:php.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿