이 글에서는 JavaScript의 비동기 프로그래밍을 소개합니다. 도움이 필요한 친구들이 모두 참고할 수 있기를 바랍니다.
Asynchronous는 비동기를 의미합니다...
이 섹션은 다소 지루할 수 있지만 JavaScript에서 매우 중요한 개념이므로 반드시 배워야 합니다.
$.ajax({ url: "www.xx.com/api", async: false, // true success: function(result) { console.log(result); }, });
// 异步批量更新DOM(vue-nextTick) // <p id="app">{{num}}</p> new Vue({ el: "#app", data: { num: 0, }, mounted() { let dom = document.getElementById("app"); while (this.num !== 100) { this.num++; } console.log("Vue num=" + this.num, "DOM num=" + dom.innerHTML); // Vue num=100,DOM num=0 // nextTick or setTimeout }, });
원인: 단일 스레드(한 시점에 한 가지 작업만 수행됨), 브라우저의 JS 엔진은 단일 스레드입니다.
단일 스레드는 JS 엔진에 IavaScript 코드를 해석하고 실행하는 스레드가 하나만 있다는 것을 의미합니다. 이를 메인 스레드라고 부를 수도 있습니다.
소위 단일 스레드는 한 번에 하나의 작업만 완료할 수 있음을 의미합니다. 여러 작업이 있는 경우 다음 작업이 실행되기 전에 이전 작업이 완료되어야 합니다.
먼저 브라우저 커널의 스레드 다이어그램을 살펴보세요.
그 중에서 렌더링 스레드와 JS 스레드는 상호 배타적입니다.
DOM 노드를 수정하는 기능과 삭제하는 기능이 동시에 있다고 가정해 보겠습니다. 여러 스레드가 있는 경우 두 스레드가 동시에 실행되면 반드시 교착 상태가 발생하게 됩니다. 문제.
JS가 싱글 스레드로 설계된 이유는 브라우저의 특수한 환경 때문입니다.
단일 스레드의 장점과 단점:
이 모드의 장점은 구현이 비교적 간단하고 실행 환경도 비교적 간단하다는 것입니다. 단점은 하나의 작업에 시간이 오래 걸리면 후속 작업을 반드시 수행해야 한다는 것입니다. 대기 상태가 되어 전체 프로그램 실행이 지연됩니다. 일반적인 브라우저 무응답(일시 중단)은 장시간 실행되는 특정 Javascript 코드 조각(예: 무한 루프)으로 인해 발생하는 경우가 많습니다. 이로 인해 전체 페이지가 이 위치에 멈춰 다른 작업을 수행할 수 없게 됩니다.
일반적인 막힘(무한 루프):
while (true) {}
JS는 원래 브라우저에서 실행되는 스크립트 언어로 설계되었기 때문에 너무 복잡하게 만들고 싶지 않아서 단일 스레드로 설계했습니다. 즉, one 그 시점에서는 딱 한 가지만 할 수 있습니다.
단일 스레드 차단을 해결이러한 단점을 해결하려면 비동기가 생성됩니다.
라면을 먹는 예를 들어보세요:
TV 시청은 비동기 작업을 수행하면 주전자가 울리게 됩니다. 이는 콜백 함수입니다.
JS의 대부분의 코드는 동기식으로 실행되며, 일부 기능만 비동기식으로 실행됩니다. 비동기식으로 실행되는 코드에는 비동기식 프로그래밍이 필요합니다.
setTimeout(() => { console.log("log2"); }, 0); console.log("log1"); // ?? log1 log2
비동기 코드의 특징: 즉시 실행되지 않고, 나중에 특정 시점에 기다려서 실행해야 합니다.
동기 코드 | 비동기 코드 |
---|---|
<script> 코드 <script> 代码 | 网络请求(Ajax) |
I/O 操作 | 定时器(setTimeout、setInterval) |
渲染操作 | Promise(then) |
async/await |
异步代码最常见的写法就是使用回调函数。
// 注意到click方法中是一个函数而不是一个变量 // 它就是回调函数 $("#btn_1").click(function() { alert("Btn 1 Clicked"); }); // 或者 function click() { // 它就是回调函数 alert("Btn 1 Clicked"); } $("#btn_1").click(click);
回调函数的缺点也很明显,容易产生回调地狱:
function getOneNews() { $.ajax({ url: topicsUrl, success: function(res) { let id = res.data[0].id; $.ajax({ url: topicOneUrl + id, success: function(ress) { console.log(ress); render(ress.data); }, }); }, }); }
function getOneNews() { axios .get(topicsUrl) .then(function(response) { let id = response.data.data[0].id; return axios.get(topicOneUrl + id); }) .then((res) => { render(res.data.data); }) .catch(function(error) { console.log(error); }); }
async function getOneNews() { let listData = await axios.get(topicsUrl); let id = listData.data.data[0].id; let data = await axios.get(topicOneUrl + id); render(data.data.data); }
预览地址:http://jsrun.net/s43Kp/embedded/all/light
如果多个异步代码同时存在,那么执行顺序应该是怎样的?那个先执行、那个后执行了?
异步代码的划分,异步代码分宏任务和微任务。
宏任务(不着急) | 微任务(着急) |
---|---|
<script> | 네트워크 요청(Ajax) |
Timer(setTimeout, setInterval) |
console.log("script start"); setTimeout(function() { console.log("timeout1"); }, 10); new Promise((resolve) => { console.log("promise1"); resolve(); setTimeout(() => console.log("timeout2"), 10); }).then(function() { console.log("then1"); }); console.log("script end");
执行顺序:
执行整体代码<script>
(宏任务)
执行所有微任务
执行一个宏任务
执行渲染线程
2->3->2->3...依次循环(在 2、3 步中又创建了新的宏、微任务)
重复从宏任务和微任务队列里拿出任务去执行。
因为浏览器设计的原因,JS 线程和渲染线程互斥,所以 JS 线程被设计成了单线程。
因为单线程执行一些操作(如网络请求)时有堵塞的问题,所有产生了异步。
因为有了异步,所以产生了异步编程,从而有了回调函数。
因为回调函数写多了会产生回调地狱,所有又有了解决回调地狱的 Promise 写法
自 ES7 标准后有了比 Promise 更加优雅的写法 ———— async/await 写法,也是异步编程的最终解决方法。
因为 JS 的代码分为同步和异步代码,同步代码的执行顺序不必多说,自上而下的执行。
但是如果有多个异步的代码,他的执行顺序又是怎么的呢??
为了解决多个异步代码的执行顺序问了,有了事件循环(EventLoop),将异步任务区分为宏任务、微任务,依据规则依次执行。
至此 完!
console.log("script start"); setTimeout(function() { console.log("timeout1"); }, 10); new Promise((resolve) => { console.log("promise1"); resolve(); setTimeout(() => console.log("timeout2"), 10); }).then(function() { console.log("then1"); }); console.log("script end");
写出 log 的输出结果,并说出理由。
更多编程相关知识,请访问:编程视频!!
위 내용은 JavaScript의 비동기 프로그래밍을 단계별로 이해해 보세요.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!