비동기 프로그래밍 예외를 해결하기 위한 자바스크립트 솔루션을 배우려면 저를 따르세요._javascript 기술
1. JavaScript 비동기 프로그래밍의 두 가지 핵심 어려움
비동기 I/O 및 이벤트 기반을 통해 단일 스레드 JavaScript를 사용하면 UI를 차단하지 않고 네트워크 및 파일 액세스 기능을 수행하고 백엔드에서 더 높은 성능을 얻을 수 있습니다. 그러나 비동기식 스타일은 몇 가지 문제를 야기합니다.
1. 함수가 너무 깊게 중첩되었습니다
JavaScript 비동기 호출은 콜백 함수를 기반으로 합니다. 여러 비동기 트랜잭션에 다중 레벨 종속성이 있는 경우 콜백 함수는 다중 레벨 중첩을 형성하고 코드는
이 됩니다.
피라미드 구조. 이로 인해 코드가 보기 흉하고 이해하기 어려워질 뿐만 아니라 디버깅 및 재구성 과정도 위험으로 가득 차게 됩니다.
2. 예외 처리
중첩된 콜백은 코드를 복잡하게 만들 뿐만 아니라 오류 처리를 더욱 복잡하게 만듭니다. 여기서는 주로 예외 처리에 대해 설명합니다.
2. 예외 처리
많은 현대 언어와 마찬가지로 JavaScript에서는 예외가 발생하고 이후에 try/catch 블록을 사용하여 포착할 수 있습니다. 던져진 예외가 포착되지 않으면 대부분의 JavaScript 환경은 유용한 스택 추적을 제공합니다. 예를 들어 다음 코드는 '{'가 잘못된 JSON 개체이기 때문에 예외를 발생시킵니다.
function JSONToObject(jsonStr) { return JSON.parse(jsonStr); } var obj = JSONToObject('{'); //SyntaxError: Unexpected end of input //at Object.parse (native) //at JSONToObject (/AsyncJS/stackTrace.js:2:15) //at Object.<anonymous> (/AsyncJS/stackTrace.js:4:11)
스택 추적은 오류가 발생한 위치뿐만 아니라 원래 잘못된 위치(코드의 4번째 줄)도 알려줍니다. 불행하게도 비동기 오류의 원인을 하향식으로 추적하는 것이 항상 간단한 것은 아닙니다.
비동기 프로그래밍에서 오류가 발생할 수 있는 두 가지 상황은 콜백 함수 오류와 비동기 함수 오류입니다.
1. 콜백 기능 오류
비동기 콜백에서 오류가 발생하면 어떻게 되나요? 먼저 테스트를 해보자.
setTimeout(function A() { setTimeout(function B() { setTimeout(function C() { throw new Error('Something terrible has happened!'); }, 0); }, 0); }, 0);
위 적용 결과는 극히 간략한 스택 추적입니다.
Error: Something terrible has happened! at Timer.C (/AsyncJS/nestedErrors.js:4:13)
잠깐, A와 B는 어떻게 됐나요? 왜 스택 추적에 나타나지 않습니까? 이는 C가 실행되면 비동기 함수의 컨텍스트가 더 이상 존재하지 않고 A와 B가 메모리 스택에 없기 때문입니다. 이 세 가지 기능은 모두 이벤트 큐에서 직접 실행됩니다. 같은 이유로 비동기 콜백에서 발생한 오류는 try/catch 블록을 사용하여 포착할 수 없습니다. 또한 콜백 함수의 반환도 의미를 잃습니다.
try { setTimeout(function() { throw new Error('Catch me if you can!'); }, 0); } catch (e) { console.error(e); }
여기서 질문이 보이시나요? 여기서 try/catch 블록은 setTimeout 함수 자체 내에서 발생하는 오류만 캡처합니다. setTimeout은 콜백을 비동기적으로 실행하기 때문에 지연이 0으로 설정된 경우에도 콜백 흐름에서 애플리케이션으로 직접 오류가 발생합니다.
일반적으로 비동기 콜백을 사용하는 함수가 try/catch 문 블록에 래핑되어 있어도 쓸모가 없습니다. (특별한 경우는 비동기식 함수가 실제로 동기식으로 작업을 수행하고 오류가 발생하기 쉬운 경우입니다. 예를 들어 Node의 fs.watch(file,callback)은 대상 파일이 존재하지 않을 때 오류를 발생시키는 함수입니다. ) 이 때문에 Node.js의 콜백은 거의 항상 오류를 첫 번째 인수로 받아들여 콜백 자체가 오류 처리 방법을 결정할 수 있습니다.
2. 비동기 함수 오류
비동기 함수는 즉시 반환되므로 비동기 트랜잭션에서 발생하는 오류는 try-catch를 통해 포착할 수 없으며 오류 처리 콜백을 제공하는 호출자에 의해서만 해결될 수 있습니다.
예를 들어 Node의 일반 함수 (err, ...) {...} 콜백 함수는 Node에서 오류를 처리하기 위한 규칙입니다. 오류는 콜백 함수의 첫 번째 실제 매개변수로 반환됩니다. 또 다른 예는 파일을 비동기적으로 읽는 동안 오류를 처리하는 데 사용되는 HTML5의 FileReader 개체의 onerror 함수입니다.
예를 들어 아래 노드 애플리케이션은 파일을 비동기식으로 읽으려고 시도하며 오류(예: "파일이 존재하지 않습니다")를 기록하는 역할도 합니다.
var fs = require('fs'); fs.readFile('fhgwgdz.txt', function(err, data) { if (err) { return console.error(err); }; console.log(data.toString('utf8')); });
클라이언트측 JavaScript 라이브러리는 일관성이 약간 떨어지지만 가장 일반적인 패턴은 성공과 실패에 대해 별도의 콜백을 지정하는 것입니다. jQuery의 Ajax 메소드는 이 패턴을 따릅니다.
$.get('/data', { success: successHandler, failure: failureHandler });
API의 모양에 관계없이 콜백에서 발생하는 비동기 오류는 콜백 내부에서만 처리될 수 있다는 점을 항상 기억하세요.
3. 잡히지 않은 예외 처리
콜백에서 예외가 발생하는 경우 콜백을 호출한 사람이 예외를 잡을 책임이 있습니다. 하지만 예외가 전혀 포착되지 않으면 어떻게 될까요? 이때 JavaScript 환경마다 게임 규칙이 다릅니다...
1. 브라우저 환경에서
최신 브라우저는 개발자 콘솔에 포착되지 않은 예외를 표시한 다음 이벤트 대기열로 돌아갑니다. 이 동작을 수정하려면 window.onerror에 핸들러를 연결하세요. windows.onerror 핸들러가 true를 반환하면 브라우저의 기본 오류 처리 동작을 방지할 수 있습니다.
window.onerror = function(err) { return true; //彻底忽略所有错误 };
在成品应用中, 会考虑某种JavaScript 错误处理服务, 譬如Errorception。Errorception 提供了一个现成的windows.onerror 处理器,它向应用服务器报告所有未捕获的异常,接着应用服务器发送消息通知我们。
2. 在Node.js 环境中
在Node 环境中,window.onerror 的类似物就是process 对象的uncaughtException 事件。正常情况下,Node 应用会因未捕获的异常而立即退出。但只要至少还有一个uncaughtException 事件处理
器,Node 应用就会直接返回事件队列。
process.on('uncaughtException', function(err) { console.error(err); //避免了关停的命运! });
但是,自Node 0.8.4 起,uncaughtException 事件就被废弃了。据其文档所言,对异常处理而言,uncaughtException 是一种非常粗暴的机制,请勿使用uncaughtException,而应使用Domain 对象。
Domain 对象又是什么?你可能会这样问。Domain 对象是事件化对象,它将throw 转化为'error'事件。下面是一个例子。
var myDomain = require('domain').create(); myDomain.run(function() { setTimeout(function() { throw new Error('Listen to me!') }, 50); }); myDomain.on('error', function(err) { console.log('Error ignored!'); });
源于延时事件的throw 只是简单地触发了Domain 对象的错误处理器。
Error ignored!
很奇妙,是不是?Domain 对象让throw 语句生动了很多。不管在浏览器端还是服务器端,全局的异常处理器都应被视作最后一根救命稻草。请仅在调试时才使用它。
四、几种解决方案
下面对几种解决方案的讨论主要集中于上面提到的两个核心问题上,当然也会考虑其他方面的因素来评判其优缺点。
1、Async.js
首先是Node中非常著名的Async.js,这个库能够在Node中展露头角,恐怕也得归功于Node统一的错误处理约定。
而在前端,一开始并没有形成这么统一的约定,因此使用Async.js的话可能需要对现有的库进行封装。
Async.js的其实就是给回调函数的几种常见使用模式加了一层包装。比如我们需要三个前后依赖的异步操作,采用纯回调函数写法如下:
asyncOpA(a, b, (err, result) => { if (err) { handleErrorA(err); } asyncOpB(c, result, (err, result) => { if (err) { handleErrorB(err); } asyncOpB(d, result, (err, result) => { if (err) { handlerErrorC(err); } finalOp(result); }); }); });
如果我们采用async库来做:
async.waterfall([ (cb) => { asyncOpA(a, b, (err, result) => { cb(err, c, result); }); }, (c, lastResult, cb) => { asyncOpB(c, lastResult, (err, result) => { cb(err, d, result); }) }, (d, lastResult, cb) => { asyncOpC(d, lastResult, (err, result) => { cb(err, result); }); } ], (err, finalResult) => { if (err) { handlerError(err); } finalOp(finalResult); });
可以看到,回调函数由原来的横向发展转变为纵向发展,同时错误被统一传递到最后的处理函数中。
其原理是,将函数数组中的后一个函数包装后作为前一个函数的末参数cb传入,同时要求:
每一个函数都应当执行其cb参数;cb的第一个参数用来传递错误。我们可以自己写一个async.waterfall的实现:
let async = { waterfall: (methods, finalCb = _emptyFunction) => { if (!_isArray(methods)) { return finalCb(new Error('First argument to waterfall must be an array of functions')); } if (!methods.length) { return finalCb(); } function wrap(n) { if (n === methods.length) { return finalCb; } return function (err, ...args) { if (err) { return finalCb(err); } methods[n](...args, wrap(n + 1)); } } wrap(0)(false); } };
Async.js还有series/parallel/whilst等多种流程控制方法,来实现常见的异步协作。
Async.js的问题:
在外在上依然没有摆脱回调函数,只是将其从横向发展变为纵向,还是需要程序员熟练异步回调风格。
错误处理上仍然没有利用上try-catch和throw,依赖于“回调函数的第一个参数用来传递错误”这样的一个约定。
2、Promise方案
ES6的Promise来源于Promise/A+。使用Promise来进行异步流程控制,有几个需要注意的问题,
把前面提到的功能用Promise来实现,需要先包装异步函数,使之能返回一个Promise:
function toPromiseStyle(fn) { return (...args) => { return new Promise((resolve, reject) => { fn(...args, (err, result) => { if (err) reject(err); resolve(result); }) }); }; }
这个函数可以把符合下述规则的异步函数转换为返回Promise的函数:
回调函数的第一个参数用于传递错误,第二个参数用于传递正常的结果。接着就可以进行操作了:
let [opA, opB, opC] = [asyncOpA, asyncOpB, asyncOpC].map((fn) => toPromiseStyle(fn)); opA(a, b) .then((res) => { return opB(c, res); }) .then((res) => { return opC(d, res); }) .then((res) => { return finalOp(res); }) .catch((err) => { handleError(err); });
通过Promise,原来明显的异步回调函数风格显得更像同步编程风格,我们只需要使用then方法将结果传递下去即可,同时return也有了相应的意义:
在每一个then的onFullfilled函数(以及onRejected)里的return,都会为下一个then的onFullfilled函数(以及onRejected)的参数设定好值。
如此一来,return、try-catch/throw都可以使用了,但catch是以方法的形式出现,还是不尽如人意。
3、Generator方案
ES6引入的Generator可以理解为可在运行中转移控制权给其他代码,并在需要的时候返回继续执行的函数。利用Generator可以实现协程的功能。
将Generator与Promise结合,可以进一步将异步代码转化为同步风格:
function* getResult() { let res, a, b, c, d; try { res = yield opA(a, b); res = yield opB(c, res); res = yield opC(d); return res; } catch (err) { return handleError(err); } }
然而我们还需要一个可以自动运行Generator的函数:
function spawn(genF, ...args) { return new Promise((resolve, reject) => { let gen = genF(...args); function next(fn) { try { let r = fn(); if (r.done) { resolve(r.value); } Promise.resolve(r.value) .then((v) => { next(() => { return gen.next(v); }); }).catch((err) => { next(() => { return gen.throw(err); }) }); } catch (err) { reject(err); } } next(() => { return gen.next(undefined); }); }); }
用这个函数来调用Generator即可:
spawn(getResult) .then((res) => { finalOp(res); }) .catch((err) => { handleFinalOpError(err); });
可见try-catch和return实际上已经以其原本面貌回到了代码中,在代码形式上也已经看不到异步风格的痕迹。
类似的功能有co/task.js等库实现。
4、ES7的async/await
ES7中将会引入async function和await关键字,利用这个功能,我们可以轻松写出同步风格的代码,
同时依然可以利用原有的异步I/O机制。
采用async function,我们可以将之前的代码写成这样:
async function getResult() { let res, a, b, c, d; try { res = await opA(a, b); res = await opB(c, res); res = await opC(d); return res; } catch (err) { return handleError(err); } } getResult();
和Generator & Promise方案看起来没有太大区别,只是关键字换了换。
实际上async function就是对Generator方案的一个官方认可,将之作为语言内置功能。
async function的缺点:
await只能在async function内部使用,因此一旦你写了几个async function,或者使用了依赖于async function的库,那你很可能会需要更多的async function。
目前处于提案阶段的async function还没有得到任何浏览器或Node.JS/io.js的支持。Babel转码器也需要打开实验选项,并且对于不支持Generator的浏览器来说,还需要引进一层厚厚的regenerator runtime,想在前端生产环境得到应用还需要时间。
以上就是本文的全部内容,希望对大家的学习有所帮助。

핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

Video Face Swap
완전히 무료인 AI 얼굴 교환 도구를 사용하여 모든 비디오의 얼굴을 쉽게 바꾸세요!

인기 기사

뜨거운 도구

메모장++7.3.1
사용하기 쉬운 무료 코드 편집기

SublimeText3 중국어 버전
중국어 버전, 사용하기 매우 쉽습니다.

스튜디오 13.0.1 보내기
강력한 PHP 통합 개발 환경

드림위버 CS6
시각적 웹 개발 도구

SublimeText3 Mac 버전
신 수준의 코드 편집 소프트웨어(SublimeText3)

뜨거운 주제











요약: C++의 비동기 프로그래밍을 사용하면 시간이 많이 걸리는 작업을 기다리지 않고 멀티태스킹이 가능합니다. 함수 포인터를 사용하여 함수에 대한 포인터를 만듭니다. 콜백 함수는 비동기 작업이 완료되면 호출됩니다. Boost::asio와 같은 라이브러리는 비동기 프로그래밍 지원을 제공합니다. 실제 사례에서는 함수 포인터와 Boost::asio를 사용하여 비동기 네트워크 요청을 구현하는 방법을 보여줍니다.

JavaScript 튜토리얼: HTTP 상태 코드를 얻는 방법, 특정 코드 예제가 필요합니다. 서문: 웹 개발에서는 서버와의 데이터 상호 작용이 종종 포함됩니다. 서버와 통신할 때 반환된 HTTP 상태 코드를 가져와서 작업의 성공 여부를 확인하고 다양한 상태 코드에 따라 해당 처리를 수행해야 하는 경우가 많습니다. 이 기사에서는 JavaScript를 사용하여 HTTP 상태 코드를 얻는 방법과 몇 가지 실용적인 코드 예제를 제공합니다. XMLHttpRequest 사용

Java 프레임워크 비동기 프로그래밍의 3가지 일반적인 문제와 해결 방법: 콜백 지옥: Promise 또는 CompletableFuture를 사용하여 보다 직관적인 스타일로 콜백을 관리합니다. 리소스 경합: 동기화 기본 요소(예: 잠금)를 사용하여 공유 리소스를 보호하고 스레드로부터 안전한 컬렉션(예: ConcurrentHashMap) 사용을 고려하세요. 처리되지 않은 예외: 작업에서 예외를 명시적으로 처리하고 예외 처리 프레임워크(예: CompletableFuture.Exceptionally())를 사용하여 예외를 처리합니다.

Go 프레임워크는 Go의 동시성 및 비동기 기능을 사용하여 동시 및 비동기 작업을 효율적으로 처리하기 위한 메커니즘을 제공합니다. 1. 동시성은 Goroutine을 통해 달성되어 동시에 여러 작업을 실행할 수 있습니다. 2. 비동기 프로그래밍은 채널을 통해 구현됩니다. 작업을 차단하지 않고 실행할 수 있습니다. 3. HTTP 요청 동시 처리, 데이터베이스 데이터의 비동기 획득 등과 같은 실제 시나리오에 적합합니다.

JavaScript에서 HTTP 상태 코드를 얻는 방법 소개: 프런트 엔드 개발에서 우리는 종종 백엔드 인터페이스와의 상호 작용을 처리해야 하며 HTTP 상태 코드는 매우 중요한 부분입니다. HTTP 상태 코드를 이해하고 얻는 것은 인터페이스에서 반환된 데이터를 더 잘 처리하는 데 도움이 됩니다. 이 기사에서는 JavaScript를 사용하여 HTTP 상태 코드를 얻는 방법을 소개하고 구체적인 코드 예제를 제공합니다. 1. HTTP 상태 코드란 무엇입니까? HTTP 상태 코드는 브라우저가 서버에 요청을 시작할 때 서비스가

1. 비동기 프로그래밍을 사용하는 이유는 무엇입니까? 기존 프로그래밍에서는 차단 I/O를 사용합니다. 즉, 프로그램은 작업을 계속하기 전에 작업이 완료될 때까지 기다립니다. 이는 단일 작업에 적합할 수 있지만 많은 수의 작업을 처리할 때 프로그램 속도가 느려질 수 있습니다. 비동기 프로그래밍은 기존 차단 I/O의 한계를 깨고 비차단 I/O를 사용합니다. 즉, 프로그램은 작업이 완료될 때까지 기다리지 않고 실행을 위해 여러 스레드나 이벤트 루프에 작업을 배포할 수 있습니다. 이를 통해 프로그램은 여러 작업을 동시에 처리할 수 있어 프로그램의 성능과 효율성이 향상됩니다. 2. Python 비동기 프로그래밍의 기본 Python 비동기 프로그래밍의 기본은 코루틴과 이벤트 루프입니다. 코루틴은 함수가 일시 중지와 재개 사이를 전환할 수 있도록 하는 함수입니다. 이벤트 루프는 일정 관리를 담당합니다.

PHP에서 비동기 프로그래밍의 장점에는 더 높은 처리량, 더 낮은 대기 시간, 더 나은 리소스 활용도 및 확장성이 포함됩니다. 단점으로는 복잡성, 디버깅의 어려움, 제한된 라이브러리 지원 등이 있습니다. 실제 사례에서 ReactPHP는 WebSocket 연결을 처리하는 데 사용되어 비동기 프로그래밍의 실제 적용을 보여줍니다.

비동기 프로그래밍, 영어 비동기 프로그래밍은 다른 작업이 완료될 때까지 기다리지 않고 프로그램의 특정 작업을 동시에 실행할 수 있어 프로그램의 전반적인 운영 효율성이 향상됨을 의미합니다. Python에서 asyncio 모듈은 비동기 프로그래밍을 구현하는 주요 도구입니다. 이는 비동기 프로그래밍에 필요한 코루틴, 이벤트 루프 및 기타 구성 요소를 제공합니다. 코루틴: 코루틴은 스레드와 마찬가지로 실행을 일시 중단했다가 재개할 수 있는 특수 함수이지만 코루틴은 스레드보다 더 가볍고 메모리를 덜 소비합니다. 코루틴은 async 키워드로 선언되고 실행은 wait 키워드에서 일시 중지됩니다. 이벤트 루프: 이벤트 루프(EventLoop)는 비동기 프로그래밍의 핵심입니다.
