Интервью: Как сбросить ошибку в генераторе?

внешний интерфейс опрос ECMAScript 6

提示: Эта статья находится на github«Понимание ECMAScript 6»Заметки организованы, и примеры кода также получены из этого. Вы можете прочитать эту книгу непосредственно, когда у вас есть время. Несмотря на то, что он на английском языке, он прост для понимания и настоятельно рекомендуется.

前情:в предыдущей статьеВы знаете, почему генераторЗдесь я бросаю много денег и знакомлюgeneratorПричины. В то время партнер указал, что «Генератор используется для имитации спящего механизма многопоточности» и «Генератор работает лениво». В то время я сказал, что в продвинутой главе будет введение, поэтому я расскажу об этом здесь.

摘要:Дело здесь прежде всего в том, какgeneratorкоммуникации, во-первых, использоватьnext()Передача параметров, во-вторых, вы также можете использоватьthrow(), отличие в том, что он закинут вовнутрь, второе в том, что естьyieldКогда оператор присваивания,generatorcoОй).

generatorНе слишком приготовлено, вы можете посмотретьздесь

1. Передача параметров

Проще говоря, вы можетеnextпередать параметры иgeneratorвнутриyieldЭтот параметр можно получить на следующем примере:

function *createIterator() {
  let first = yield 1;
  let second = yield first + 2;    // 4 + 2
  yield second + 3;                // 5 + 3
}
let iterator = createIterator();   
console.log(iterator.next());      // "{ value: 1, done: false }"
console.log(iterator.next(4));     // "{ value: 6, done: false }"
console.log(iterator.next(5));     // "{ value: 8, done: false }"
console.log(iterator.next());      // "{ value: undefined, done: true }"

Процесс реализации

Чтобы наглядно объяснить описанный выше процесс выполнения, вы можете использовать эту схему:

Те, у кого одинаковый цвет, выполняются в одной итерации, от светлого к темному, с указанием порядка итераций. Такие как:

  1. первый звонокnext(), воплощать в жизньyield 1остановиться, вернуться{ value: 1, done: false }.УведомлениеЗатем заявление о назначенииlet fisrt = ...Не реализована;
  2. второй звонокnext(4), сначала установите параметр4yieldможно понимать как:
let first = yield 1;

=>

let first = 4;

Затем начните выполнение с того места, где была последняя пауза, то есть сначала выполните оператор присваивания.

let first = 4

затем переходите к следующемуyieldпока, т.е.

yield first + 2  // 4 + 2

последнее возвращение{ value: 6, done: false }

ПослеnextВыполнять по указанному выше принципу до завершения итерации.

То есть поnextпараметр,generatorпроизведеноiterator, построенный с внешней средоймост связи, в сочетании сiteratorФункция, которая может делать паузу, может делать некоторые интересные вещи, такие как запись обратных вызовов синхронным способом, подробности см. Ниже.

2. кiteratorжеребьевка

function *createIterator() {
  let first = yield 1;
  let second = yield first + 2; // yield 4 + 2, 然后抛出错误
  yield second + 3;             // 不会被执行
}
let iterator = createIterator();
	
console.log(iterator.next());  // {value: 1, done: false}
console.log(iterator.next(4)); // {value: 6, done: false}
console.log(iterator.throw(new Error("Boom"))); // generator 里抛出的错误

В соответствии с механизмом выполнения, упомянутым выше, поток выполнения этого примера может быть представлен этой диаграммой:

В третий раз, когда выполняется итерация, мы вызываемiterator.throw(new Error("Boom")), КiteratorВозникает ошибка, и переданный параметр является сообщением об ошибке.

мы можем преобразоватьcreateIteratorследующим образом:

function* createIterator() {
  let first = yield 1;
  let second;
  try {
    second = yield first + 2;
  } catch (ex) {
    second = 6;
  }
  yield second + 3;
}
let iterator = createIterator();                 
console.log(iterator.next());                   // "{ value: 1, done: false }"
console.log(iterator.next(4));                  // "{ value: 6, done: false }"
console.log(iterator.throw(new Error("Boom"))); // "{ value: 9, done: false }"
console.log(iterator.next());                   // "{ value: undefined, done: true }"

Его поток выполнения объясняется следующим образом:

  1. первые два звонкаnextСитуация такая же, как анализ в механизме исполнения выше, поэтому я не буду вспоминаться.

  2. третий звонокiterator.throw(new Error("Boom")прошлоеgeneratorВыдает ошибку внутри функции, где она в последний раз остановилась, т.е.yield first + 2Получить информацию, выдать ошибку. но былcatch, поэтому продолжите выполнение до следующей паузы:

    yield second + 3;  // 6 + 3
    

    Наконец, верните результат этой итерации{ value: 9, done: false }

  3. Продолжайте выполнять другие итерации, ничем не отличающиеся от вышеописанных, поэтому я не буду их повторять.

резюме:Здесь вы можете видеть,next()а такжеthrow()Может сделатьiteratorПродолжайте выполнять, разница в том, что последнее будет выполнено с выдачей ошибкиiteratorпродолжать выполнять. Но после этого,generatorЧто здесь происходит, зависит от того, как написан код.

3. Generatorвнутреннийreturnутверждение

здесьreturnОператоры, функционально идентичные обычным функциямreturnЭто не имеет большого значения, он остановитсяreturnПосле того, как утверждение выполнено.

function* createIterator() {
  yield 1;
  return;
  yield 2;
  yield 3;
}
let iterator = createIterator();
console.log(iterator.next());  // "{ value: 1, done: false }"
console.log(iterator.next());  // "{ value: undefined, done: true }"

надreturnПосле этогоyieldОни игнорируются, поэтому вторая итерация и умерла.

но еслиreturnПосле того, как имеет значение, результат будет учитываться в текущей итерации:

function* createIterator() {
  yield 1;
  return 42;
}
let iterator = createIterator();
console.log(iterator.next());  // "{ value: 1, done: false }"
console.log(iterator.next());  // "{ value: 42, done: true }"
console.log(iterator.next());  // "{ value: undefined, done: true }"

этоiteratorreturnзначение после{ value: 42, done: true }.

Кроме того, это возвращаемое значение можно использовать только один раз, поэтому третье выполнениеnext, возвращаемый результат становится{ value: undefined, done: true }.

обращать внимание:спред оператор...а такжеfor-ofувидеть повторяющиеся результатыdoneдаtrueЯ немедленно прекращу реализацию.returnПоследнее значение также игнорируется и останавливается очень решительно. Как и в примере выше, используйтеfor-ofа также...воплощать в жизнь:

function* createIterator() {
  yield 1;
  return 42;
}

let iterator = createIterator();

for(let item of iterator) {
  console.log(item);
}
// 1

let anotherIterator = createIterator();
console.log([...anotherIterator]) 
// [1]

// 猜猜 [...iterator] 的结果是什么

4. Генератор делегата

generatorЧто такое делегирование, проще говоряgeneratorделегатыgeneratorB, пусть B сделает это за вас:

function* createNumberIterator() {
  yield 1;
  yield 2;
}
function* createColorIterator() {
  yield "red";
  yield "green";
}
function* createCombinedIterator() {
  yield* createNumberIterator();
  yield* createColorIterator();
  yield true;
}

var iterator = createCombinedIterator();

console.log(iterator.next()); // "{ value: 1, done: false }"
console.log(iterator.next()); // "{ value: 2, done: false }"
console.log(iterator.next()); // "{ value: "red", done: false }"
console.log(iterator.next()); // "{ value: "green", done: false }"
console.log(iterator.next()); // "{ value: true, done: false }"
console.log(iterator.next()); // "{ value: undefined, done: true }"

Как видно из вышеизложенного, синтаксис делегирования таков, что вgeneratorв, сyield*управлять другимgeneratorрезультат исполнения.

Делегируя различныеgeneratorсобрать и использовать повторноreturnВозвращаемое значение, вы можетеgenerator

function* createNumberIterator() {
  yield 1;
  yield 2;
  return 3;
}
function* createRepeatingIterator(count) {
  for (let i = 0; i < count; i++) {
    yield "repeat";
  }
}
function* createCombinedIterator() {
  let result = yield* createNumberIterator();
  yield* createRepeatingIterator(result);
}
var iterator = createCombinedIterator();
console.log(iterator.next()); // "{ value: 1, done: false }"
console.log(iterator.next()); // "{ value: 2, done: false }"
console.log(iterator.next()); // "{ value: "repeat", done: false }"
console.log(iterator.next()); // "{ value: "repeat", done: false }"
console.log(iterator.next()); // "{ value: "repeat", done: false }"
console.log(iterator.next()); // "{ value: undefined, done: true }"

Как указано выше,createNumberIteratorВозвращаемое значение3Он был представленcreateRepeatingIterator, если разобрать и написать, то вот так:

function* createNumberIterator() {
  yield 1;
  yield 2;
  return 3;
}

function* createRepeatingIterator(count) {
  for (let i = 0; i < count; i++) {
    yield "repeat";
  }
}

function* createCombinedIterator() {
  let result = yield* createNumberIterator();
  yield result;
  yield* createRepeatingIterator(result);
}

var iterator = createCombinedIterator();
console.log(iterator.next());  // "{ value: 1, done: false }"
console.log(iterator.next());  // "{ value: 2, done: false }"
console.log(iterator.next());  // "{ value: 3, done: false }"
console.log(iterator.next());  // "{ value: "repeat", done: false }"
console.log(iterator.next());  // "{ value: "repeat", done: false }"
console.log(iterator.next());  // "{ value: "repeat", done: false }"
console.log(iterator.next());  // "{ value: undefined, done: true }"

Примечание: посколькуyield *с последующимgeneratorрезультат выполнения иgeneratorдаiterable. то есть,yield *может непосредственно следоватьiterable, как строка. Такие как:

  let g = function *() {
    yield *['a', 'b', 'c']
  }

  for(let item of g()) {
    console.log(item);
  }
  
  // a
  // b
  // c

5. GenaratorАсинхронный

оjsХарактеристики асинхронности в этом расширены здесь. Проще говоря, это позволяетjsЭти однопоточные языки более мощные, однако, когда асинхронная ситуация сложна, например зависимости между асинхронностью, легко написать следующееcallback hell, крайне сложно поддерживать:

Разумное использованиеgenarator

genaratorвернутьiterator, Вам нужно вручную вызватьnextКуча проблем.那如果封装一些,可以让iteratorЭто нормально, это хорошо:

  1. Предварительная подготовка, автоматическое выполнениеgeneratorФункция

    run(function* () {
      let value = yield 1;
      console.log(value);
      value = yield value + 3;
      console.log(value);
    });
    

    Чтобы заставить его выполнить себя, затемrunнужно:

    1. воплощать в жизньgenerator, получатьiterator;
    2. передачаiterator.next();
    3. Используйте возвращаемый результат предыдущего шага в следующий разiterator.next(lastResult)параметры, продолжайте итерацию;
    4. Повторяйте 3, пока итерация не будет завершена.

    Реализация выглядит следующим образом:

    function run(taskDef) {
      
      // 创建并保存 iterator,留到后面使用
      let task = taskDef();
      
      let result = task.next();
      
      // 递归地执行 `next`
      function step() {
        // 如果没完的话
        if (!result.done) {
          result = task.next(result.value);
          step();
        }
      }
      // 开始处理
      step();
    }
    
  2. Добиваемся цели, пишем асинхронно синхронно

    Присоединяйтесь к нам, чтобы заставить работать следующий код:

    const asyncWork = new Promise((resolve, reject) => {
      setTimeout(() => resolve(5), 500)
    })
    
    
    run(function* () {
      let value = yield asyncWork;
      console.log(value)
      value = yield value + 3;
      console.log(value)
    });
    
    

    Здесь и предыдущий пример отличается от того,yieldВозвращаемый результат может бытьpromise

    if (result.value && typeof result.value.then === 'function') {
      result.value.then(d => {
        result = task.next(d)
        ... 
      })
    }
    

    promise, воплощать в жизньthenфункция, передать возвращаемый результат следующей итерацииnext(d)Вот и все. Полный пример кода выглядит следующим образом:

    function run(taskDef) {
      
      // 创建并保存 iterator,留到后面使用
      let task = taskDef();
      
      let result = task.next();
      
      // 递归地执行 `next`
      function step() {
        
        // 如果没完的话
        if (!result.done) {
          if (result.value && typeof result.value.then === 'function') {
            result.value.then(d => {
              result = task.next(d)
              step();
            })
          } else {
            result = task.next(result.value);
            step();
          }
        }
      }
      // 开始处理
      step();
    }
    

    Посмотрите еще раз на это письмо:

    run(function* () {
      let value = yield asyncWork;
      console.log(value)
      value = yield value + 3;
      console.log(value)
    });
    

    Хотя второйyieldк предыдущемуyieldРезультат зависит, но его не нужно записывать как обратный вызов, он выглядит так же, как синхронизация, очень просто!

Эпилог

generatorпроизведеноiterator, Можно использоватьnext, К внешней функцииgeneratorданные могут передаваться черезthrowВставьте неправильный. они эквивалентныgeneratorНесколько коммуникационных окон открыты внутри и снаружи, что делает возможной явную асинхронность. мощныйredux-sagaтакже на основеgeneratorосуществленный. Есть ли другие способы играть? Все просто бросать кирпичи, чтобы привлечь Джейд, я не знаю, есть ли у вас другие способы игры?

если правильноgeneratorПроисхождение не ясно, вы также можете взглянуть наздесь

Кроме того, эта статья была впервые опубликована вgithub, околоES6серия статей. Помогите, если можетеstarДа ладно, работу найти проще. Эй, ищу работу, правда-устал-ах! ! !