js 异步迭代器

同步的迭代器模式

众所周知,迭代器模式就是在已有的 对象里 增加一个 迭代函数,然后 通过迭代函数 来执行 对应的函数

const obj = {
  arr1: [1,2,3,4,5],
  arr2: [1,2,3,4,5],
  next(fn) {
    const arr = [...this.arr1, ...this.arr2]
    for (val of arr) {
      fn(val)
    }
  }
}

obj.next(console.log)

异步的迭代器模式

先讲讲 不使用es6 的情况下的做法

// 这里是被异步调用的函数 
function promise1(next, value) {
    setTimeout(() => {
        console.log(1, value)
        next(2)
    }, 100);
}
function promise2(next, value) {
    setTimeout(() => {
        console.log(2, value)
        next(3)
    }, 100);
}

var events = [promise1, promise2]

做法一


function eventLoop(events, initv) {
    let index = 0;
    function next(value) {
        if (index >= events.length) {
            return value
        } else {
            const fn = events[index]
            fn(next, value)
        }
        index ++
    }
    next(initv)
}

eventLoop(events, 1)

这里是基于 约定 的方式 来进行的,也就是说,你异步执行完成以后,一定要执行 传过来的 next 函数,否则 进行不下去,是不是很熟悉?

没错了,vue-router 和 koa2 的 use 都是基于这种写法的

做法二

这个就是 很有名的 redux compose 中的做法 了,实际上 这个也是基于 约定的方式 来实现的 ,不过要注意的是,如果没有那些 约定的话,这个不是 异步的,这里就不深入讲了,要不然光光这个就可以写一两篇单独的博客了

 function compose(...funcs) {
  if (funcs.length === 0) {
    return arg => arg
  }

  if (funcs.length === 1) {
    return funcs[0]
  }

  // 每个函数的返回值 都会被 当做 一个
  return funcs.reduce((a, b) => (...args) => a(b(...args)))
}

然后看一个最简单的中间件 redux-thunk

// 整个过程就是 柯里化 的过程,巧妙地运用了闭包的 概念
function createThunkMiddleware(extraArgument) {
  // 返回的函数 其实 是一个 三重返回的 函数
  // 第一重是 用于 redux 中,收集 dispatch 和 store 的闭包
  // const chain = middlewares.map(middleware => middleware(middlewareAPI))
  // 第二重 就是 用来执行 上文 中的 compose 的,注意这里的 next , 
  // 其实就是传进来的 需要执行的下一个函数
  // 第三重,就是 接受 dispatch('dosomething') 中的 dosomething 这个参数的
  return ({ dispatch, getState }) => (next) => (action) => {
    if (typeof action === 'function') {
      return action(dispatch, getState, extraArgument);
    }

    return next(action);
  };
}

const thunk = createThunkMiddleware();
thunk.withExtraArgument = createThunkMiddleware;

export default thunk;

其实都是将接下来的代码的 执行 权利 和时机交给上一个函数 判断

 


那使用了 es6 之后,又会有怎样的精彩呢?

// 注意,这里的函数的 返回值 都是 一个 Promise
function promise1(value) {
    return new Promise(resolve => {
        setTimeout(() => {
            console.log(1, value)
            resolve(1)
        }, 100);
    })
}
function promise2(value) {
    return new Promise(resolve => {
        setTimeout(() => {
            console.log(2, value)
            resolve(2)
        }, 100);
    })
}

var events = [promise1, promise2]

做法一

既然都说了迭代器了,那当然得 出来 es6 之中的 generator 了

function * generatorf(events, val) {
    for (fn of events) {
        val = yield fn(val)
    }
}
let g = generatorf(events, 1);
function cnext(result) {
    if (result.done) return;
    result.value.then((val) => {
        cnext(g.next(val))
    }) 
}
cnext(g.next())

可以看到,这里相比于之前,执行 next 的权利被进行了 转移,到了一个专门的函数里面了,当然,如果想要在代码里面获得 next 的控制权,也可以避免使用 cnext,而是 直接在每一个 Promise 的异步函数之中,手动的去调用 g.next()

而使用的场景,类似于我司的 用户校验,是否已经注册了?注册的信息是否完善?是否是 18岁以上?等等

这样的话,每一个 都有额外的 判断和流程图 要走,自然是 将控制权放在代码里更 方便

做法二

关于 generator 之上,还要 es7 中的 async 和 await 来进行 进一步的封装,这样的话,就简单地只需要几行代码就够了,但是这样的话,就获得不到 函数的 next 控制权了,只能交给 函数一步一步去执行

async function generatorf(events, val) {
    for (fn of events) {
        val = await fn(val)
    }
}
generatorf(events, 1);

做法三

做法3其实使用到了 Symbol 关键字

凭什么 异步调用 还和 这东西扯上关系了?看一下 阮一峰 老师 的 es6

没错了,看到这里有没有 心头一颤?和之前 的 generator 很像,但是完全 去除了  generator 的辅助

let g = events[Symbol.iterator](1);
function cnext(result) {
    if (result.done) return;
    result.value(1).then((val) => {
        cnext(g.next(val))
    }) 
}
cnext(g.next())

当然,因为这里的数据都被包在了 数组里,所以看不出来,但是 如果 需要执行的 异步函数被写在了一个 配置 JSON 里面呢?

例如:

const events = {
    steps1: {
        fn: promise1
    },
    steps2: {
        fn: promise2
    },
    [Symbol.iterator]() {
        const entries = Object.entries(this);
        let index = 0;
        return {
            next() {
                if (index < entries.length) {
                    const value = entries[index][1].fn
                    index ++
                    return {
                        value,
                        done: false
                    };
                } else {
                    return { value: undefined, done: true };
                }
            }
        };
    }
}
async function gen (events, val) {
    for (const iterator of events) {
        val = await iterator(val)
    }
}
gen(events, 1)

这样就完成了 对于配置项的 异步迭代

当然,如果你想要 在函数里面获得 对应的 操作的权利,那么 就应该换一种写法了


const events = {
    steps1: {
        fn: promise1
    },
    steps2: {
        fn: promise2
    }
}
function * gen (events, val) {
    for (const iterator in events) {
        console.log(iterator)
        val = yield events[iterator].fn(val)
        // val = await iterator(val)
    }
}
var g = gen(events, 1)

然后异步执行 的函数 应该这样写,就好像 上面 不适用 es6 的时候一样,需要手动去执行是否next

// 注意,这里的函数的 都需要去执行 一个 g.next 来决定 是否继续往下走
function promise1(value) {
        setTimeout(() => {
            console.log(1, value)
             if (true) g.next() 
             else // 做其他的操作
        }, 100);
}
function promise2(value) {
        setTimeout(() => {
            console.log(1, value)
             if (true) g.next() 
             else // 做其他的操作
        }, 100)
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值