博客 / 詳情

返回

【js】異步迭代器(async Iterator)

異步迭代器(async Iterator)

  1. 同步迭代器
  2. 異步迭代器
  3. for await...of
  4. 異步生成器函數
  5. yield*語句

1.同步迭代器

  • 普通的迭代器生成函數在被調用後會返回一個迭代器對象,可以去調用迭代器上的next方法
  • next方法一旦執行,就必須同步地得到一個狀態對象,{value,done}

    //迭代器生成函數
    function makeIterator(arr) {
      var nextIndex = 0;
      return {
          next() {
              return nextIndex < arr.length ?
                  { value: arr[nextIndex++], done: false } :
                  { value: undefined, done: true }
          }
      }
    }
    //調用並遍歷
    const arr = [1]
    let iter = makeIterator(arr)
    console.log(iter.next()) //{value: 1, done: false}
    console.log(iter.next());//{value: undefined, done: true}

2.異步迭代器

讓next返回的value是一個Promise對象,用then鏈式處理

//迭代器生成函數
function makeIterator(arr) {
    var nextIndex = 0;
    let delay = 4000
    return {
        next() {
            let res = nextIndex < arr.length ?
                {
                    value: new Promise(resolve => setTimeout(() => resolve(arr[nextIndex++]), delay -= 1000)),
                    done: false
                }
                : { value: undefined, done: true }
            return res
        }
    }
}
//調用並遍歷
const arr = [1, 2]
let iter = makeIterator(arr)
iter.next().value.then(val => console.log(val)) //1
iter.next().value.then(val => console.log(val));//2
iter.next().value.then(val => console.log(val));//undefined
這種方法只是讓value異步執行,done的值並不是異步產生的,並且執行了流程不清晰,語義比較繞

next直接返回一個Promise,用then的鏈式處理異步

//迭代器生成函數
function asyncIterator(arr) {
    var nextIndex = 0
    return {
        [Symbol.asyncIterator]() {
            return {
                next() {
                    if (nextIndex < arr.length) {
                        return new Promise(resolve => {
                            setTimeout(() => resolve({ value: arr[nextIndex++], done: false }), 1000)
                        })
                    } else {
                        return { value: undefined, done: true }
                    }
                }
            }
        }
    }
}
//鏈式執行
const asyncIterable = asyncIterator([1, 2])
const asyncIter = asyncIterable[Symbol.asyncIterator]()
asyncIter
    .next()
    .then(val1 => {
        console.log(val1);
        return asyncIter.next()
    })
    .then(val2 => {
        console.log(val2);
        return asyncIter.next()
    })
    .then(val3 => {
        console.log(val3);
    })

//{value: 1, done: false}
//{value: 2, done: false}
//{value: undefined, done: true}
既然asyncIter返回的是一個Promise,那麼執行可以用async改寫,能得到一致的結果
async function runner() {
    const asyncIterable = asyncIterator([1, 2])
    const asyncIter = asyncIterable[Symbol.asyncIterator]()
    let val1 = await asyncIter.next()
    console.log(val1);
    console.log(await asyncIter.next());
    console.log(await asyncIter.next());
}

3 for await...of

根據上面的迭代器生成函數,Symbol.asyncIterator是一個異步迭代器的接口,返回一個迭代器對象,可以用for await...of去遍歷這個接口

async function runner() {
    for await (const i of asyncIterator([1, 2])) {
        console.log(i);
    }
}
runner()
//1
//2
  • asyncIterator([1, 2])返回一個對象,這個對象擁有異步迭代器接口,能返回一個異步迭代器對象
  • 這個對象的異步迭代器的next方法被for...of循環自動調用會得到一個Promise 對象
  • await用來處理這個 Promise 對象,在resolve之後把得到的值i傳入for...of的循環體
遍歷出錯會終止for await...of,並報錯
可以將for await..of放在try中,用catch捕獲錯誤

4.異步生成器函數

同步的生成器函數會生成一個同步迭代器,那麼異步生成器則生成異步迭代器
作用
Generator 函數處理同步操作和異步操作時,能夠使用同一套接口

特點
異步生成器函數就是async和Generator的結合

function promise(data, delay) {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve(data)
        }, delay)
    })
}
function printHello() {
    return promise("Hello", 1000)
}
function printWorld() {
    return promise("World", 1000)
}
async function* gene() {
    yield printHello()
    yield printWorld()
}
let iter = gene()
iter.next()
    .then(val => { console.log(val); })//{value:"Hello",done:false}
  • 異步的生成器函數返回一個迭代器對象,調用next方法,返回的是一個Promise,用then能取到狀態對象
  • 同步的生成器函數返回一個迭代器對象,調用next方法,直接返回狀態對象,狀態對象中的value是一個Promise
  • 使用for await...of可以異步的遍歷同步迭代器,但是隻有value是異步的,done的生成仍然是同步的

    async function runner() {
      for await (const i of iter) {
          console.log(i);
      }
    }
    runner()
    不管Generator函數前是否有async都會分別延遲1000ms打印Hello與World

await 和 yield可以同時出現在異步Generator函數中

async function* readLines(path) {
  let file = await fileOpen(path);

  try {
    while (!file.EOF) {
      yield await file.readLine();
    }
  } finally {
    await file.close();
  }
}
await後面的操作返回一個Promise對象,將外部操作產生的值輸入函數內部
yield命令會返回一個Promise,作為每個next暫停的位置,將函數內部的值輸出
  • async函數和異步Generator函數都是對異步操作的封裝
  • async函數自帶執行器
  • 異步Generator函數可以用for await...of去遍歷

函數就可以分為四類

  1. 普通函數
  2. async函數
  3. Generator函數
  4. 異步Generator函數

5. yield* 語句

yield* 後可以跟一個Generator函數,將其語句展開到異步生成器內

function* gene1() {
    yield 'a'
    yield 'b'
    console.log(0);
}
async function* gene2() {
    yield* gene1()
    yield 'c';
}
async function runner() {
    for await (const i of gene2()) {
        console.log(i);
    }
}
runner()
//"a"
//"b"
//0
//"c"
user avatar hightopo 頭像 201926 頭像 icezero 頭像 william_wang_5f4c69a02c77b 頭像 heptagon 頭像 clearlove07 頭像 bupthly 頭像 yxaw 頭像 acfasj 頭像 grewer 頭像 blake-lundquist 頭像 wswx 頭像
14 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.