#72 使用 generator 模拟 async/await


  • 0
    administrators

    在远古时代,我们使用 callback 进行异步流程控制,但是会有 callback hell 的问题。经过历史的发展,逐渐地使用了不少的工具进行异步流程的改进,例如 Async.js、Promise 等,到后来的 generator + promise,还有最终的方案 async/await。了解以前是用什么方案处理异步流程控制,对我们理解现在的 asyn/await 也是很有好处。

    请你实现一个简单的函数 wrapAsync,使用 generator + promise 来模拟 async/await 进行异步流程的控制。wrapAsync 接受一个 generator 函数作为参数,并且返回一个函数。generator 函数内部可以使用关键字 yield 一个 Promise 对象,并且可以类似 async/await 那样获取到 Promise 的返回结果,例如:

    const getData = (name) => {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          resolve('My name is ' + name)
        }, 100) // 模拟异步获取数据
      })
    }
    
    const run = wrapAsync(function * (lastName) {
      const data1 = yield getData('Jerry ' + lastName)
      const data2 = yield getData('Lucy ' + lastName)
      return [data1, data2]
    })
    
    run('Green').then((val) => {
      console.log(val) // => [ 'My name is Jerry Green', 'My name is Lucy Green' ]
    })
    

    getData 是一个异步函数并且返回 Promise,我们通过 yield 关键字获取到这个异步函数的 Promise 返回的结果,在代码编写上起来像是同步的,执行上实际是异步的。

    请你完成 wrapAsync 的编写,wrapAsync 返回的函数接受的参数和传入的 generator 接受的函数保持一致,并且在调用的时候会传给 generator 函数(正如上面的例子);另外,wrapAsync 返回的函数执行结果是一个 Promise,我们可以通过这个 Promise 获取到 generator 函数执行的结果(正如上面的例子)。

    (此简单实现你暂时不需要考虑异常的控制。)


  • 0

    @胡子大哈 "wrapAsync应返回一个函数"是怎么检测的

    const wrapAsync = generatorFn => async (...param) => {
      const gen = generatorFn(...param)
      let state = gen.next()
      while (!state.done)
        state = gen.next(await state.value)
      return state.value
    }
    

    这么写为什么会爆这个错误呢


  • 0
    administrators

    @CodeHz 这里不能用 async 函数,只能用普通的函数。出错信息应该更明确一点的。


  • 0

    @ScriptOJ 有点简单的模拟co库的意思啊这一题,给个解法:

    const wrapAsync = (fn) => {
        return (...args) => {
            const gen = fn.apply(null, args);
            return new Promise((resolve, reject) => {
                let g = null;
    
                const fullFill = (res) => {
                    g = gen.next(res);
                    next(g);
                };
    
                fullFill();
    
                function next(ret) {
                    if (ret.done) return resolve(ret.value);
                    return ret.value.then(fullFill);
                }
            });
        };
    };
    

  • 0

    另外 报个bug吧,用户排行的分页有点问题,分页多了,后面几页没有用户,是不是在读取数据时,没有排除分数为0的用户啊~


  • 0

    @ScriptOJ

    Wrong Answer
    调用 wrapAsync 返回的函数所获得的 Promise 的 then 获得的数据 和 generator 返回的数据并不一致,你确定你 yield 出正确的数据了吗?

    报了以上错误,不造为何,在Chrome上跑以下代码结果是和题目一样的

    const wrapAsync = (generatorFn) => {
      return function (...p) {
        let g = generatorFn.apply(null, p)
        let n = g.next()
        var a = []
        while (!n.done) {
          a.push(n.value)
          n = g.next()
        }
        return new Promise(resolved => {
          resolved(Promise.all(a))
        })
      }
    }
    

    被题主的这个方法坑死了

    const run = wrapAsync(function * (lastName) {
      const data1 = yield getData('Jerry ' + lastName)
      const data2 = yield getData('Lucy ' + lastName)
      return [data1, data2]
    })
    

    一直以为data1和data2能接收到Promise,结果是undefined,最后自己创建个数组保存这些Promise,Chrome运行正确,这里却报了最上面那段错误,何解???????


  • 0

    @终天霸主 本来yield的返回值就是undefined呀,他只会根据你next函数的参数作为返回值的


  • 1

    写了好久

    const wrapAsync = (generatorFn) => {
      //是否循环
      let isLoop = true
      //返回的闭包
      return function resultFunc(){
        //得到iterator
        let argumentsArr = Array.prototype.slice.call(arguments)
        let generatorIterator = generatorFn(...argumentsArr);
        //闭包返回的promise
        return new Promise((resolve,reject)=>{
        function promiseFunc(promiseResult){
          let generatorIteratorObj = generatorIterator.next(promiseResult)
          if(!generatorIteratorObj.done){
            generatorIteratorObj.value.then(promiseFunc)
          }else{
            resolve(generatorIteratorObj.value)
          }
        }
          generatorIterator.next().value.then(promiseFunc);
        })
      };
    }
    

  • 0

    var wrapAsync=function(fun){
    	function donext(gen,a,v=undefined){
    		let ret=gen.next(v);
    		if(ret.value instanceof Promise){
    			ret.value.then(x=>donext(gen,a,x))
    		}else{
    			a(ret.value);
    		}
    	}
    	return function(inp){
    		return new Promise(
    			function(a,b){
    				var gen=fun(inp);
    				donext(gen,a);
    			}
    		);
    	}
    }
    

登录后回复
 

与 ScriptOJ 的连接断开,我们正在尝试重连,请耐心等待