欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

JavaScript生成器函數(shù)Generator解決異步操作問(wèn)題

 更新時(shí)間:2023年10月30日 08:58:02   作者:Tqing  
這篇文章主要為大家介紹了JavaScript生成器函數(shù)Generator解決異步操作問(wèn)題示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

為什么使用Generator?

在JavaScript使用異步操作時(shí),在async和await還沒(méi)有被JavaScript官方正式推出時(shí),那么異步操作解決方案就只有回調(diào)函數(shù)和Promise。

回調(diào)函數(shù)

所謂回調(diào)函數(shù),就是把需要執(zhí)行的動(dòng)作以函數(shù)的方式包裝起來(lái),再將這個(gè)函數(shù)以參數(shù)的方式傳遞給其他的函數(shù),當(dāng)時(shí)機(jī)到來(lái)時(shí)再進(jìn)行調(diào)用。

// 需在瀏覽器中運(yùn)行
function loadImage(imgUrl, callback) {
  const img = document.createElement("img");
  img.onload = function () {
    callback(this);
  };
  img.src = imgUrl;
}
loadImage(
  "https://travel.12306.cn/imgs/resources/uploadfiles/images/1716878f-79a2-4db1-af8c-b9c2039f0b3c_product_W572_H370.jpg",
  (img) => {
    document.body.appendChild(img);
    loadImage(
      "https://travel.12306.cn/imgs/resources/uploadfiles/images/8b36f9a7-f780-4e71-b719-9300109a9ff2_product_W572_H370.jpg",
      (img) => {
        document.body.appendChild(img);
        loadImage(
          "https://travel.12306.cn/imgs/resources/uploadfiles/images/6d77d0ea-53d0-4518-b7e9-e53795b4920c_product_W572_H370.jpg",
          (img) => {
            document.body.appendChild(img);
          }
        );
      }
    );
  }
);

Promise

promise是為了解決回調(diào)函數(shù)產(chǎn)生的回調(diào)地獄問(wèn)題而產(chǎn)生的。

function loadImage(imgUrl) {
  return new Promise((resolve) => {
    const img = document.createElement("img");
    img.onload = function () {
      resolve(this);
    };
    img.src = imgUrl;
  });
}
loadImage(
  "https://travel.12306.cn/imgs/resources/uploadfiles/images/1716878f-79a2-4db1-af8c-b9c2039f0b3c_product_W572_H370.jpg"
)
  .then((img) => {
    document.body.appendChild(img);
    return loadImage(
      "https://travel.12306.cn/imgs/resources/uploadfiles/images/8b36f9a7-f780-4e71-b719-9300109a9ff2_product_W572_H370.jpg"
    );
  })
  .then((img) => {
    document.body.appendChild(img);
    return loadImage(
      "https://travel.12306.cn/imgs/resources/uploadfiles/images/6d77d0ea-53d0-4518-b7e9-e53795b4920c_product_W572_H370.jpg"
    );
  })
  .then((img) => {
    document.body.appendChild(img);
  });

雖然解決了回調(diào)地獄的問(wèn)題,異步任務(wù)執(zhí)行步驟也更加清晰了,但是相比于現(xiàn)在async和await的異步操作同步化的表達(dá)方式還是略遜一籌,在async和await還沒(méi)有推出時(shí),社區(qū)就已經(jīng)利用生成器函數(shù)實(shí)現(xiàn)了社區(qū)版的async和await,當(dāng)時(shí)生成器的出現(xiàn)就是為了服務(wù)于異步編程。

使用方式

創(chuàng)建方式

function* getValue() {
  yield 1;
  yield 2;
  return 3;
}
const generator = getValue() // 獲取到生成器
console.log(Object.prototype.toString.call(generator)); // [object Generator]

next方法

獲取返回值

生成器函數(shù)和普通函數(shù)的調(diào)用方式完全不同,上面代碼雖然調(diào)用生成器函數(shù)產(chǎn)生了生成器,但是生成器函數(shù)內(nèi)部的代碼并為被執(zhí)行。

那么需要怎樣操作才能讓生成器內(nèi)部的代碼執(zhí)行?

生成器有一個(gè)next方法,當(dāng)這個(gè)方法被調(diào)用時(shí),會(huì)把yield后面的值返回回來(lái),并且生成器函數(shù)內(nèi)部的代碼停止執(zhí)行,當(dāng)再次調(diào)用next方法后,生成器函數(shù)內(nèi)部代碼會(huì)從上次暫停處開(kāi)始執(zhí)行,到下一個(gè)yield語(yǔ)句處停止。

next()方法返回的對(duì)象包含兩個(gè)屬性:

  • value:返回的值,也就是yield關(guān)鍵字后面的值。

done:表示生成器函數(shù)是否已經(jīng)完成,true表示已經(jīng)完成,false表示未完成。

function* getValue() {
yield 1;
yield 2;
yield 3;
}
const generator = getValue();
console.log(generator.next()); // { value: 1, done: false }
console.log(generator.next()); // { value: 2, done: false }
console.log(generator.next()); // { value: 3, done: false }
console.log(generator.next()); // { value: undefined, done: true }

當(dāng)生成器內(nèi)最后的yield語(yǔ)句也執(zhí)行過(guò)后,意味著整個(gè)生成器函數(shù)執(zhí)行完畢,調(diào)用next()方法產(chǎn)出的結(jié)果中done的值為true了。

簡(jiǎn)單用圖片演示下運(yùn)行流程:

第一次調(diào)用next()方法,生成器函數(shù)會(huì)暫停在第一個(gè)yield語(yǔ)句

再次調(diào)用next()方法,生成器函數(shù)會(huì)暫停在第二個(gè)yield語(yǔ)句

再次調(diào)用next()方法,生成器函數(shù)會(huì)暫停在第三個(gè)yield語(yǔ)句

再次調(diào)用next()方法,生成器函數(shù)執(zhí)行完畢

這里返回的value為什么是undefined,其實(shí)可以這么理解,因?yàn)樵贘avaScript中,函數(shù)不主動(dòng)return,那么會(huì)默認(rèn)返回undefined。

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

如果生成器函數(shù)中有return語(yǔ)句,那么在執(zhí)行return語(yǔ)句的時(shí)候就會(huì)把生成器的狀態(tài)置為已完成,后面的yield語(yǔ)句將不再被執(zhí)行,這點(diǎn)和普通函數(shù)是保持一致的。

function* getValue() {
  yield 1;
  yield 2;
  return 3;
}
const generator = getValue();
console.log(generator.next()); // { value: 1, done: false }
console.log(generator.next()); // { value: 2, done: false }
console.log(generator.next()); // { value: 3, done: true }

傳遞參數(shù)

next函數(shù)其實(shí)也可以傳遞參數(shù),這個(gè)參數(shù)將被yield語(yǔ)句消費(fèi)。

function* getValue() {
  const a = yield 1;
  const b = yield 2 * a;
  return 3 * b;
}

const generator = getValue();

console.log(generator.next()); // { value: 1, done: false }
console.log(generator.next(5)); // { value: 10, done: false }
console.log(generator.next(6)); // { value: 18, done: true }

還是使用圖片來(lái)演示整個(gè)流程。

  • 首先調(diào)用next(),語(yǔ)句在第一個(gè)yield語(yǔ)句處暫停
  • 然后調(diào)用next(5),參數(shù)5會(huì)傳遞給yield語(yǔ)句等號(hào)左邊的變量a,語(yǔ)句會(huì)停在第二個(gè)yield語(yǔ)句處
  • 然后調(diào)用next(6),參數(shù)6會(huì)傳遞給yield語(yǔ)句等號(hào)左邊的變量b,碰到return語(yǔ)句直接返回,整個(gè)生成器函數(shù)執(zhí)行完畢。

throw函數(shù)

next函數(shù)可以往生成器里面?zhèn)鬟f函數(shù),而throw方法可以往生成器函數(shù)里面拋出異常,如果沒(méi)有在生成器函數(shù)里面捕獲異常,那么生成器函數(shù)會(huì)向其它函數(shù)一樣拋出異常。

function* getValue() {
  yield 1;
  try {
    yield 2;
  } catch (error) {
    console.error(error); // 捕獲異常, 打印 trhow error
  }
  yield 3;
}

const generator = getValue();

console.log(generator.next()); // { value: 1, done: false }
console.log(generator.next(5)); // { value: 2, done: false }
console.log(generator.throw("throw error")); // {value: 3, done: false}

return函數(shù)

reutrn函數(shù)在被調(diào)用后,生成器函數(shù)會(huì)直接返回return函數(shù)傳遞的值,而且生成器函數(shù)整個(gè)函數(shù)執(zhí)行完畢。

function* getValue() {
  yield 1;
  yield 2;
  yield 3;
}
const generator = getValue();
console.log(generator.next()); // { value: 1, done: false }
console.log(generator.return(5)); // { value: 5, done: true } // 此時(shí)生成器函數(shù)執(zhí)行完畢
console.log(generator.next()); // {value: undefined, done: true}

使用場(chǎng)景

實(shí)現(xiàn)Async和Await

function getValue(n, ms = 1000) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(n);
    }, ms);
  });
}
function* generator() {
  console.log(1);
  let x = yield getValue(2);
  console.log(x);
  x = yield getValue(3, 2000);
  console.log(x);
  x = yield 4;
  console.log(x);
}
function asyncGenerator(generator) {
  return new Promise((resolve, reject) => {
    let iterable = generator();
    let generated = iterable.next();
    tick();
    function tick() {
      if (generated.done === false) {
        Promise.resolve(generated.value).then(
          (value) => {
            try {
              generated = iterable.next(value);
              tick();
            } catch (err) {
              reject(err);
            }
          },
          (reason) => {
            try {
              generated = iterable.throw(reason);
              tick();
            } catch (err) {
              reject(err);
            }
          }
        );
      } else {
        resolve(generated.value);
      }
    }
  });
}
asyncGenerator(generator);

借用上面實(shí)現(xiàn)asyncGenerator函數(shù),我們可以再來(lái)優(yōu)化下加載圖片的代碼

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body></body>
  <script>
    const imgUrlList = [
      "https://travel.12306.cn/imgs/resources/uploadfiles/images/1716878f-79a2-4db1-af8c-b9c2039f0b3c_product_W572_H370.jpg",
      "https://travel.12306.cn/imgs/resources/uploadfiles/images/8b36f9a7-f780-4e71-b719-9300109a9ff2_product_W572_H370.jpg",
      "https://travel.12306.cn/imgs/resources/uploadfiles/images/6d77d0ea-53d0-4518-b7e9-e53795b4920c_product_W572_H370.jpg",
    ];

    function loadImage(imgUrl) {
      return new Promise((resolve) => {
        const img = document.createElement("img");
        img.onload = function () {
          resolve(this);
        };
        img.src = imgUrl;
      });
    }

    function* loadImageList() {
      const img1 = yield loadImage(imgUrlList[0]);
      document.body.appendChild(img1);
      const img2 = yield loadImage(imgUrlList[1]);
      document.body.appendChild(img2);
      const img3 = yield loadImage(imgUrlList[2]);
      document.body.appendChild(img3);
    }

    function asyncGenerator(generator) {
      return new Promise((resolve, reject) => {
        let iterable = generator();
        let generated = iterable.next();
        tick();

        function tick() {
          if (generated.done === false) {
            Promise.resolve(generated.value).then(
              (value) => {
                try {
                  generated = iterable.next(value);
                  tick();
                } catch (err) {
                  reject(err);
                }
              },
              (reason) => {
                try {
                  generated = iterable.throw(reason);
                  tick();
                } catch (err) {
                  reject(err);
                }
              }
            );
          } else {
            resolve(generated.value);
          }
        }
      });
    }

    asyncGenerator(loadImageList).then(() => {
      console.log("load success");
    });
  </script>
</html>

實(shí)現(xiàn)自定義迭代器

const list = {
  head: {
    value: 1,
    next: {
      value: 2,
      next: {
        value: 3,
        next: null,
      },
    },
  },
  *[Symbol.iterator]() {
    let curNode = list.head;
    while (curNode) {
      yield curNode.value;
      curNode = curNode.next;
    }
  },
};
for (let val of list) {
  console.log(val);
}

上面針對(duì)鏈表數(shù)據(jù)結(jié)構(gòu)使用for...of的進(jìn)行遍歷,這得益于在list內(nèi)部聲明了迭代器,有兩點(diǎn)是for...of所需要的

  • 返回包含next方法的對(duì)象
  • 調(diào)用返回的next方法返回的對(duì)象包含value和done這兩個(gè)屬性

而這兩點(diǎn)生成器函數(shù)全部滿足,無(wú)疑是天作之合。

以上就是JavaScript生成器函數(shù)Generator解決異步操作問(wèn)題的詳細(xì)內(nèi)容,更多關(guān)于JavaScript生成器函數(shù)Generator的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論