웹사이트 개발에서 비동기 이벤트는 프로젝트가 반드시 처리해야 하는 링크입니다. 또한 프론트엔드 프레임워크의 등장으로 인해 프레임워크를 통해 구현된 SPA는 웹사이트를 빠르게 구축하기 위한 표준이 되었습니다. ; 이 기사에서는 JavaScript의 비동기 처리에 대해 설명합니다.
우선 동기화와 비동기가 각각 무엇을 가리키는지 이해해야 합니다.
초심자에게는 이 두 용어가 항상 혼란스럽습니다. 결국 중국어의 문자 그대로의 의미는 정보 과학의 관점에서 보면 동기화라는 것은 여러 가지를 동시에 처리하는 것을 의미합니다. 평행한.
예를 들어, 업무를 처리하기 위해 은행에 갈 때 창구 앞에 줄을 서는 것이 동기 실행이고, 번호를 받고 다른 일을 먼저 하는 것이 JavaScript의 비동기 이벤트인 Event Loop의 특성을 통한 비동기 실행입니다. 케이크 조각이라고 할 수 있습니다
그럼 JavaScript에서 비동기 이벤트를 처리하는 방법은 무엇일까요?
우리에게 가장 친숙한 함수는 콜백 함수입니다. 예를 들어, 웹 페이지가 사용자와 상호 작용할 때 등록된 이벤트 리스너는 콜백 함수를 수신해야 하거나 setTimeout
및 xhr
와 같은 다른 웹 API 함수도 콜백을 수신해야 합니다. 함수. 사용자가 요구하는 시간에 콜백 함수를 전달하여 트리거할 수 있습니다. 먼저 setTimeout
의 예를 살펴보겠습니다. setTimeout
、xhr
,也都能通过传递回调函数在用户要求的时机去触发。先看一个 setTimeout
的例子:
// callback function withCallback() { console.log('start') setTimeout(() => { console.log('callback func') }, 1000) console.log('done') }withCallback() // start // done // callback func
在 setTimeout
被执行后,当过了指定的时间间隔之后,回调函数会被放到队列的末端,再等待事件循环处理到它。
注意:也就时因为这种机制,开发者设定给 setTimeout
的时间间隔,并不会精准的等于从执行到触发所经过的时间,使用时要特别注意!
回调函数虽然在开发中十分常见,但也有许多难以避免的问题。例如由于函数需要被传递给其他函数,开发者难以掌控其他函数内的处理逻辑;又因为回调函数仅能配合 try … catch
捕捉错误,当异步错误发生时难以控制;另外还有最著名的“回调地狱”。
幸好在 ES6 之后出现了 Promise,拯救了身陷在地狱的开发者们。其基本用法也很简单:
function withPromise() { return new Promise(resolve => { console.log('promise func') resolve() }) } withPromise() .then(() => console.log('then 1')) .then(() => console.log('then 2')) // promise func // then 1 // then 2
之前讨论 Event Loop 时没有提到的是,在HTML 5 的Web API 标准 中,Event Loop 新增了微任务队列(micro task queue),而 Promise 正是通过微任务队列来驱动它的;微任务队列的触发时机是在栈被清空时,JavaScript 引擎会先确认微任务队列有没有东西,有的话就优先执行,直到清空后才从队列拿出新任务到栈上。
如上面的例子,当函数回传一个 Promise 时,JavaScript 引擎便会把后传入的函数放到微任务队列中,反复循环,输出了上列的结果。后续的 .then
语法会回传一个新的 Promise,参数函数则接收前一个 Promise.resolve
的结果,凭借这样函数参数传递,让开发者可以管道式的按顺序处理异步事件。
如果在例子中加上 setTimeout
就更能清楚理解微任务与一般任务的差别:
function withPromise() { return new Promise(resolve => { console.log('promise func') resolve() }) } withPromise() .then(() => console.log('then 1')) .then(() => setTimeout(() => console.log('setTimeout'), 0)) .then(() => console.log('then 2')) // promise func // then 1 // then 2 -> 微任务优先执行 // setTimeout
另外,前面所说的回调函数很难处理的异步错误,也可以通过 .catch
语法来捕获。
function withPromise() { return new Promise(resolve => { console.log('promise func') resolve() }) } withPromise() .then(() => console.log('then 1')) .then(() => { throw new Error('error') }) .then(() => console.log('then 2')) .catch((err) => console.log('catch:', err)) // promise func // then 1 // catch: error // ...error call stack
从 ES6 Promise 问世之后,异步代码从回呼地狱逐渐变成了优雅的函数式管道处理,但对于不熟悉度的开发者来说,只不过是从回调地狱变成了 Promise 地狱而已。
在 ES8 中规范了新的 async
/await
,虽然只是 Promise 和 Generator Function组合在一起的语法糖,但通过 async
/await
便可以将异步事件用同步语法来处理,就好像是老树开新花一样,写起来的风格与 Promise 完全不同:
function wait(time, fn) { return new Promise(resolve => { setTimeout(() => { console.log('wait:', time) resolve(fn ? fn() : time) }, time) }) } await wait(500, () => console.log('bar')) console.log('foo') // wait: 500 // bar // foo
通过把 setTimeout
包装成 Promise,再用 await
关键字调用,可以看到结果会是同步执行的先出现 bar
,再出现 foo
,也就是开头提到的将异步事件写成同步处理。
再看一个例子:
async function withAsyncAwait() { for(let i = 0; i < 5; i++) { await wait(i*500, () => console.log(i)) } }await withAsyncAwait() // wait: 0 // 0 // wait: 500 // 1 // wait: 1000 // 2 // wait: 1500 // 3 // wait: 2000 // 4
代码中实现了withAsyncAwait
函数,用 for
循环及 await
关键字反复执行 wait
函数;此处执行时,循环每次会按顺序等待不同的秒数再执行下一次循环。
在使用 async
/await
时,由于 await
rrreee
setTimeout
이 실행된 후 지정된 시간 간격이 지나면 콜백 함수가 setTimeout의 끝에 배치됩니다. 대기열에 넣은 다음 이벤트 루프가 이를 처리할 때까지 기다립니다. 🎜참고: 이 메커니즘으로 인해 개발자가 setTimeout
에 설정한 시간 간격은 실행에서 트리거까지 경과된 시간과 정확히 동일하지 않습니다. !
🎜콜백 함수는 개발에서 매우 일반적이지만, 피하기 어려운 문제도 많이 있습니다. 예를 들어, 함수는 다른 함수로 전달되어야 하기 때문에 개발자가 다른 함수의 처리 로직을 제어하기 어렵고 콜백 함수는 잡기 위해 try...catch
에만 협력할 수 있기 때문입니다. 오류, 비동기 오류가 발생할 때 제어하기가 어렵습니다. 또한 가장 유명한 "콜백 지옥"도 있습니다. 🎜🎜🎜Promise🎜🎜🎜다행히 ES6 이후 등장한 Promise는 지옥에 갇힌 개발자들을 구해냈습니다. 기본 사용법도 매우 간단합니다. 🎜rrreee🎜이전에 Event Loop를 논의할 때 언급되지 않은 것은 HTML 5 Web API 표준에서 Event Loop가 마이크로 작업 대기열을 추가했고 Promise는 마이크로 작업 대기열을 사용한다는 것입니다. 태스크 큐는 스택이 지워질 때 트리거됩니다. JavaScript 엔진은 먼저 마이크로 태스크 큐에 아무것도 없는지 확인합니다. 스택에서 지워질 때까지 대기열에서 새 작업을 수행합니다. 🎜🎜위의 예에서와 같이 함수가 Promise를 반환하면 JavaScript 엔진은 나중에 전달된 함수를 마이크로태스크 대기열에 넣고 반복적으로 루프를 실행하며 위에 나열된 결과를 출력합니다. 후속 .then
구문은 새 Promise를 반환하고 매개변수 함수는 이전 Promise.resolve
의 결과를 수신합니다. 이 함수 매개변수 전달을 통해 개발자는 핸들을 파이프라인할 수 있습니다. 비동기 이벤트를 순차적으로 처리합니다. 🎜🎜예제에 setTimeout
을 추가하면 마이크로태스크와 일반태스크의 차이를 더욱 명확하게 이해할 수 있습니다. 🎜rrreee🎜그리고 앞서 언급한 콜백 함수로는 처리하기 어려운 비동기 오류도 .catch
구문을 전달하여 캡처할 수도 있습니다. 🎜rrreee🎜🎜async wait🎜🎜🎜ES6 Promise의 등장 이후 비동기 코드는 점차 콜백 지옥에서 우아한 기능적 파이프라인 처리로 바뀌었지만, 익숙하지 않은 개발자에게는 그저 콜백 지옥의 변화일 뿐입니다. 그냥 지옥을 약속해. 🎜🎜새로운 async
/await
는 ES8에서 표준화되었습니다. 이는 단지 Promise와 Generator 함수를 결합하기 위한 구문 설탕일 뿐이지만 async
를 통해 전달됩니다. > /await
는 오래된 나무가 새 꽃을 피우는 것처럼 동기 구문을 사용하여 비동기 이벤트를 처리할 수 있습니다. 작성 스타일은 Promise와 완전히 다릅니다. 🎜rrreee🎜setTimeout code>를 사용합니다. Promise로 패키징된 후 <code>await
키워드를 사용하여 호출하면 bar
가 먼저 나타나고 foo가 나타나는 것을 볼 수 있습니다.
가 나타납니다. 즉, 처음에 언급한 동기 처리에 비동기 이벤트를 작성하는 것입니다. 🎜🎜또 다른 예를 보세요: 🎜rrreee🎜withAsyncAwait
함수가 코드에 구현되어 있고 for
루프와 await
키워드가 사용됩니다. wait
함수를 반복적으로 실행하려면 여기에서 실행하면 루프는 다음 루프를 실행하기 전에 순서대로 다른 시간(초) 동안 대기합니다. 🎜🎜async
/await
를 사용할 때 await
키워드는 async 함수에서만 실행될 수 있으므로 꼭 기억하세요. 같은 시간. 🎜또한 루프를 사용하여 비동기 이벤트를 처리하는 경우 ES6 이후에 제공되는 많은 Array 메서드는 지원되지 않으며async
/await
语法,如果这里用 forEach
取代 for
결과는 동기 실행이 되어 0.5초마다 숫자가 인쇄된다는 점에 유의해야 합니다.
이 기사는 간단합니다. 이 기사에서는 JavaScript가 비동기 처리를 처리하는 세 가지 방법을 소개하고, 앞서 언급한 이벤트 루프를 반영하는 몇 가지 간단한 예를 통해 코드 실행 순서를 설명하고 여기에 마이크로태스크 대기열의 개념을 추가합니다. 동기식 및 비동기식 애플리케이션을 이해하는 데 도움이 되기를 바랍니다.
더 많은 프로그래밍 관련 지식을 보려면 프로그래밍 소개를 방문하세요! !
위 내용은 JavaScript에서 비동기가 처리되는 방식 이해의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!