欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

异步迭代器,generator,for await of

程序员文章站 2022-03-27 20:42: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) => {...

这些东西好像看起来抽象,其实不然。现在假设有这么一个业务场景,有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