簡體   English   中英

JavaScript 向迭代器添加“return”方法不能正確關閉迭代器

[英]JavaScript adding “return” method to an iterator doesn't properly close the iterator

我正在學習 JavaScript ES6 迭代器模式並遇到了這個問題:

const counter = [1, 2, 3, 4, 5];
const iter = counter[Symbol.iterator]();
iter.return = function() {
  console.log("exiting early");
  return { done: true };
};

for (let i of iter) {
  console.log(i);
  if (i >= 3) {
    break;
  }
}
console.log('---');
for (let i of iter) {
  console.log(i);
}

// 1
// 2
// 3
// exiting early
// ---
// 4
// 5

因此,我向從數組中提取的迭代器添加了return方法定義。 盡管調用了 return 方法,但它實際上並沒有關閉迭代器。 相比之下,如果我在定義中定義迭代器return方法,它將按預期工作:

class Counter {
  [Symbol.iterator]() {
    let count = 1;
    return {
      next() {
        if (count <= 5) {
          return {
            done: false,
            value: count++
          }
        } else {
          return {
            done: true,
            value: undefined
          }
        }
      },
      return() {
        console.log('exiting early');
        return { done: true, value: undefined };
      }
    }
  }
}

const myCounter = new Counter();
iter = myCounter[Symbol.iterator]();
for (let i of myCounter) {
  console.log(i);
  if (i >= 3) {
    break;
  }
}
console.log('---');
for (let i of myCounter) {
  console.log(i);
}

// 1
// 2
// 3
// exiting early
// ---
// 1
// 2
// 3
// 4
// 5

我的問題是,為什么我會出現這種意外行為? 我假設如果沒有調用return方法,那么迭代器將不會關閉,直到它通過調用next到達最后。 但是添加return屬性將正確地“調用” return方法,因為我得到了控制台日志,但即使我在return方法中返回{ done: true }也不會真正終止迭代器。

您的兩個return方法都沒有真正關閉迭代器。 為了實現這一點,他們需要記錄迭代器的新 state,從而導致next方法在所有后續調用中也返回{done: true} - 這就是“關閉”的實際含義。

我們可以通過生成器看到這種行為:

 const iter = function*(){ yield 1; yield 2; yield 3; }(); console.log(iter.next()); console.log(iter.return()); console.log(iter.next());

您的第一個片段存在您已覆蓋iter.return的問題,並且您的方法被調用(從日志中可以看出)但它實際上從未關閉iter 根本問題是數組迭代器無法關閉,它們通常根本沒有return方法。 您還必須覆蓋iter.next方法來模擬這一點。

您的第二個片段的問題是它實際上並沒有嘗試迭代iter ,但它迭代了myCounter兩次,這為每個循環創建了一個新的迭代器 object 。 相反,我們需要使用[Symbol.iterator]方法多次返回相同的 object,最簡單的方法是讓Counter實現迭代器接口本身。 我們現在可以重現意外的行為:

 class Counter { count = 1; [Symbol.iterator]() { return this; } next() { if (this.count <= 5) { return {done: false, value: this.count++ }; } else { return {done: true, value: undefined}; } } return() { console.log('exiting early'); return { done: true, value: undefined }; } } const iter = new Counter(); for (let i of iter) { console.log(i); if (i >= 3) { break; } } console.log('---'); for (let i of iter) { console.log(i); }

為了解決這個問題,我們將通過讓return方法將計數設置為超過 5 來關閉迭代器:

 class Counter { count = 1; [Symbol.iterator]() { return this; } next() { if (this.count <= 5) { return {done: false, value: this.count++ }; } else { return {done: true, value: undefined}; } } return() { this.count = 6; // ^^^^^^^^^^^^^^^ console.log('exiting early'); return { done: true, value: undefined }; } } const iter = new Counter(); for (let i of iter) { console.log(i); if (i >= 3) { break; } } console.log('---'); for (let i of iter) { console.log(i); // not executed! }

您的示例可以簡化為

let count = 1;
const iter = {
  [Symbol.iterator]() { return this; },
  next() {
    if (count <= 5) {
      return {
        done: false,
        value: count++
      }
    } else {
      return {
        done: true,
        value: undefined
      }
    }
  },
  return() {
    console.log('exiting early');
    return { done: true, value: undefined };
  }
};

for (let i of iter) {
  console.log(i);
  if (i >= 3) {
    break;
  }
}
console.log('---');
for (let i of iter) {
  console.log(i);
}

所以iter只是一個普通的 object。 您將它傳遞給for..of循環兩次。

您對迭代器接口的工作方式做出了錯誤的假設。 核心問題是這段代碼中沒有任何內容存儲和跟蹤iter返回done: true的事實,因此應該繼續這樣做。 如果這是你想要的行為,你需要自己做,例如

let count = 1;
let done = false;
const iter = {
  [Symbol.iterator]() { return this; },
  next() {
    if (!done && count <= 5) {
      return {
        value: count++
      }
    } else {
      done = true;
      return { done };
    }
  },
  return() {
    done = true;
    console.log('exiting early');
    return { done };
  }
};

for..of循環本質上調用.next()直到返回結果done: true ,並且在某些情況下調用.return 由迭代器本身的實現來確保它正確地進入一個“封閉的”state。

所有這一切也可以通過使用生成器 function 來簡化,因為生成器對象具有內部“關閉”state 作為 ZC1C425268E68385D1AB5074C17A94 F14 的副作用自動包含在內

function* counter() {
  let counter = 1;
  while (counter <= 5) yield counter++;
}

const iter = counter();
for (let i of iter) {
  console.log(i);
  if (i >= 3) {
    break;
  }
}
console.log('---');
for (let i of iter) {
  console.log(i);
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM