>  기사  >  웹 프론트엔드  >  자바스크립트에서 async 사용법에 대한 자세한 설명

자바스크립트에서 async 사용법에 대한 자세한 설명

巴扎黑
巴扎黑원래의
2017-08-23 14:00:153891검색

이 글은 주로 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 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.