异步迭代器,generator,for await of

2022-07-28,,

这些东西好像看起来抽象,其实不然。现在假设有这么一个业务场景,有n个文件,需要先删除第1个、再删除第2个、第3个...第n个,你会怎么写?

已知如下文件列表,删除函数,该函数是异步的

const files = Array(100).fill(0).map((t, i) => ('file' + i));
// 删除文件函数
function deleteFile(index) {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            console.log(`已删除第${index+1}个文件`);
            resolve();
        }, 50);
    })
}

写法一:

一开始我是这么写的,就是一个递归写法,判断删完没有,没有就继续删

let deletedCount = 0;

async function handler(index) {
    await deleteFile(index);
    if (deletedCount < files.length - 1) {
        deletedCount++;
        await handler(index + 1);
    }
}
handler(0);

后面想到可以利用数组reduce,每次返回一个promise,目标结构如下

Promise.resolve()
    .then(() => {
        deleteFile(1)
    }).then(() => {
        deleteFile(2)
    }).then(() => {
        deleteFile(3)
    }).then(() => {
        deleteFile(4)
    })
//...

故有,写法二

function handler2(files) {
    let index = 0;
    return files.reduce(async(prev, cur) => {
        return prev.then(await deleteFile.bind(null, index++))
            //这是下面这种的简化形式
            // return prev.then(async() => {
            //     return await deleteFile(index++);
            // })
    }, Promise.resolve())
}

这两种写法都挺好的,缺少点流程控制的感觉吧,于是for await of 登场了。

先了解一下generator ,简单理解generator函数就是,第一次调用它会返回一个可迭代对象,但并不执行函数里的代码,后面依次调用这个对象的next方法,就会执行 yield 关键字 前面的代码。

可迭代对象的好处就是,这把一个函数里面的执行权交给了外部,我们可以随时停止,也可以随时调用next再恢复执行。

写法三

function* createObject(files) {
    // 怎么写不重要,这里目的根据files个数,生成多少个yield语句
    // 相当于 yield deleleFile(0)
    // yield deleleFile(1) yield deleleFile(2)...
    for (const index in files) {
        yield deleteFile(parseInt(index));
    }
}
//生成一个可迭代对象
let object = createObject(files);

async function handler3() {
    // 遍历该对象,就是依次调用 yield后面的代码
    for await (const iterator of object) {
        
    }
}
handler3()

怎么理解可迭代对象?其实没啥,它就是一个对象,之所以他能迭代,普通对象不能迭代,就是因为它多了个特殊的属性,属性名叫Symbol.iterator,值为一个函数,然后就是generator函数,所以下面这种写法和上面是等价的

let object = {
}
object[Symbol.iterator] = function*() {
    for (const index in files) {
        yield deleteFile(parseInt(index));
    }
}
async function handler3() {
    // 遍历该对象,就是依次调用 yield后面的代码
    for await (const iterator of object) {
        
    }
}
handler3()

以后遇到需要控制异步流程,可以优先想到用迭代器,这样看起来很清晰。

个人感觉这3种写法,优先级:迭代器>回调>reduce合成

迭代器清晰流畅就不用说了,回调写法看起来也还好,reduce合成属于装逼技巧,但是不能那么直观地看出控制流程。

 

 

 

 

 

本文地址:https://blog.csdn.net/illusion_melody/article/details/109265717

《异步迭代器,generator,for await of.doc》

下载本文的Word格式文档,以方便收藏与打印。