이 글은 주로 javascript async를 이해하는 사용법을 소개합니다. 편집자는 이것이 꽤 좋다고 생각합니다. 이제 여러분과 공유하고 참고할 것입니다. 에디터를 따라가서 살펴보겠습니다.
Written in front
이 글에서는 파일을 순차적으로 읽는 최적의 방법을 구현해보겠습니다. 구현 방법은 가장 오래된 콜백 방법부터 현재 비동기까지 다양합니다. 썽크 라이브러리와 공동 라이브러리에 대한 나의 이해. 달성된 효과: a.txt 및 b.txt를 순차적으로 읽고 읽은 내용을 문자열로 연결합니다.
동기 읽기
const readTwoFile = () => { const f1 = fs.readFileSync('./a.txt'), f2 = fs.readFileSync('./b.txt'); return Buffer.concat([f1, f2]).toString(); };
이 방법은 코드도 너무 명확하고, 중첩도 잘 안 되지만, 유지 관리도 잘 되어 있다는 점이 가장 큰 문제입니다. 성능은 집약적인 I/O를 처리하기 위한 비동기 I/O이며 동기 읽기는 서버의 CPU를 크게 낭비하므로 이 방법을 사용하면 됩니다. (실제로 노드의 모든 비동기 프로그래밍 솔루션의 목표는 동기 의미 체계 및 비동기 실행을 달성하는 것입니다.)
콜백을 사용하여 읽기
const readTwoFile = () => { let str = null; fs.readFile('./a.txt', (err, data) => { if (err) throw new Error(err); str = data; fs.readFile('./b.txt', (err, data) => { if (err) throw new Error(err); str = Buffer.concat([str, data]).toString(); }); }); };
콜백을 사용하면 구현이 매우 간단합니다. 그냥 중첩하면 됩니다. 직접적으로 전달하지만 이 경우 유지 관리도 어렵고 이해도 어려운 상황이 발생하기 쉽습니다. 가장 극단적인 경우는 콜백 지옥입니다.
Promise 구현
const readFile = file => new Promise((reslove, reject) => { fs.readFile(file, (err, data) => { if (err) reject(err); reslove(data); }); }); const readTwoFile = () => { let bf = null; readFile('./a.txt') .then( data => { bf = data; return readFile('./b.txt'); }, err => { throw new Error(err) } ) .then( data => { console.log(Buffer.concat([bf, data]).toString()) }, err => { throw new Error(err) } ); };
Promise는 수평적 성장 콜백을 수직적 성장으로 변환하여 일부 문제를 해결할 수 있지만 Promise로 인한 문제는 언뜻 보면 코드 중복입니다. 멋지지만 콜백 함수 중첩에 비해 크게 개선되었습니다.
yield
Generator는 본질적으로 코루틴입니다. 코루틴, 스레드 및 프로세스 간의 차이점과 연결을 살펴보겠습니다.
프로세스: 기본 리소스 할당 운영체제 유닛
스레드: 운영체제의 자원 스케줄링의 기본 단위
코루틴: 자체 CPU 컨텍스트, 코루틴 1개, 스택 1개를 갖춘 스레드보다 작은 실행 단위
존재할 수 있음 프로세스에는 하나의 스레드에 여러 개의 스레드와 여러 개의 코루틴이 있을 수 있습니다. 프로세스와 스레드의 전환은 운영 체제에 의해 제어되는 반면, 코루틴의 전환은 프로그래머가 직접 제어합니다. 비동기 I/O는 콜백을 사용하여 집중적인 I/O를 처리할 수도 있습니다. 코루틴을 전환해도 I/O 작업을 코루틴에 작성하고 다음과 같이 진행할 수 있습니다. 아, CPU를 다른 코루틴에 양도할 수 있습니다.
js는 Yield인 코루틴도 지원합니다. Yield를 사용하면 이 위치에서 실행이 중지되고 다른 코드가 계속 실행되기를 원할 때 계속해서 실행된다는 느낌이 듭니다.
function *readTwoFile() { const f1 = yield readFile('./a.txt'); const f2 = yield readFile('./b.txt'); return Buffer.concat([f1, f2]).toString(); }
yield에서의 순차 읽기도 순차 읽기 방법입니다.
thunkify를 사용하는 방법은
const thunkify = (fn, ctx) => (...items) => (done) => { ctx = ctx || null; let called = false; items.push((...args) => { if (called) return void 0; called = true; done.apply(ctx, args); }); try { fn.apply(ctx, items); } catch(err) { done(err); } };
thunkify 함수를 사용하는 것입니다. , 마지막 입력 매개변수는 콜백 함수입니다. thunkify를 사용하면 Yield 함수의 자동화된 프로세스를 쉽게 구현할 수 있습니다.
const run = fn => { const gen = fn(); let res; (function next(err, data) { let g = gen.next(data); if (g.done) return void 0; g.value(next); })(); };
Use Promise
const readFile = file => new Promise((reslove, reject) => { fs.readFile(file, (err, data) => { if (err) reject(err); reslove(data); }); }); const run = fn => { const gen = fn(); let str = null; (function next(err, data) { let res = gen.next(data); if (res.done) return void 0; res.value.then( data => { next(null, data); }, err => { throw new Error(err); } ); })(); }; run(readTwoFile);
위의 두 가지 방법 모두 프로세스를 달성할 수 있습니다. 자동으로 Yield를 실행하는 방법이 있는데, 이 두 가지 구현 방법과 호환되는 방법이 있나요? Master TJ가 다른 라이브러리인 co 라이브러리를 먼저 살펴보겠습니다.
// readTwoFile的实现与上面类似,readFile既可以利用Promise也可以利用thunkify // co库返回一个Promise对象 co(readTwoFile).then(data => console.log(data));
co 라이브러리의 구현을 살펴보세요. co 라이브러리는 기본적으로 Promise 객체를 반환합니다(예: 위의 res.value). co 라이브러리는 이를 Promise로 변환합니다. 구현 아이디어는 기본적으로 재귀를 사용하여 매우 간단합니다. 일반적인 아이디어는 다음과 같습니다.
const baseHandle = handle => res => { let ret; try { ret = gen[handle](res); } catch(e) { reject(e); } next(ret); }; function co(gen) { const ctx = this, args = Array.prototype.slice.call(arguments, 1); return new Promise((reslove, reject) => { if (typeof gen === 'function') gen = gen.apply(ctx, args); if (!gen || typeof gen.next !== 'function') return resolve(gen); const onFulfilled = baseHandle('next'), onRejected = baseHandle('throw'); onFulfilled(); function next(ret) { if (ret.done) reslove(ret.value); // 将yield的返回值转换为Proimse const value = toPromise.call(ctx, ret.value); if (value && isPromise(value)) return value.then(onFulfilled, onRejected); return onRejected(new TypeError('yield type error')); } }); }
toPromise는 일부 유형을 Promise로 변환하는 것입니다. 여기서는 어떤 유형이 Yield 뒤에 배치될 수 있는지 확인할 수 있습니다. 일반적으로 사용되는 것을 살펴보세요:
// 把thunkify之后的函数转化为Promise的形式 function thunkToPromise(fn) { const ctx = this; return new Promise(function (resolve, reject) { fn.call(ctx, function (err, res) { if (err) return reject(err); if (arguments.length > 2) res = slice.call(arguments, 1); resolve(res); }); }); }
최근 Node는 비동기 작업을 수행하는 데 사용할 수 있는 async/await를 지원했습니다.
궁극의 솔루션
const readFile = file => new Promise((reslove, reject) => { fs.readFile(file, (err, data) => { if (err) reject(err); reslove(data); }); }); const readTwoFile = async function() { const f1 = await readFile('./a.txt'); const f2 = await readFile('./b.txt'); return Buffer.concat([f1, f2]).toString(); }; readTwoFile().then(data => { console.log(data); });
async/await의 기능 Promise는 호출 메서드를 피하면서 객체를 직렬로 연결하는 것입니다. 코드는 매우 읽기 쉽고 동기식 메서드입니다. 콜백 문제를 우아하게 해결하기 위해 더 이상 다른 외부 라이브러리(예: 공동 라이브러리)에 의존할 필요가 없습니다
위 내용은 자바스크립트에서 async 사용법에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!