用Generator函数和Promise对async/await进行模拟与转换

算法流星
• 阅读 1305

Talk is cheap,show you the code

//异步函数1
function doLazy1() {
    return new Promise((rs, rj) => {
        setTimeout(() => {
            console.log('do Lazy1');
            rs();
        }, 1000)
    })
}
//异步函数2
function doLazy2() {
    return new Promise((rs, rj) => {
        setTimeout(() => {
            console.log('do Lazy2');
            rs();
        }, 2000)
    })
}
//同步函数
function doNow() {
    console.log('do now!');
    return 'now'
}

 async function doAsync() {
    console.log('start');
    await doLazy1();
    console.log('after Lazy1');
    await doLazy2();
    console.log('after Lazy2');
    const res = await doNow();
    console.log('after now');
    return res;
}
doAsync();

//打印出:
// start
// do Lazy1
// after Lazy1
// do Lazy2
// after Lazy2
// do now!
// after now
// 表达式输出为
// "now"

console.log('-----------------');

function runGenerator(generator) {
    const task = generator();
    let lastStep = task.next();

    function stepFoward() {
        if (lastStep.done === false) {
            return Promise.resolve(lastStep.value).then((res) => {
                lastStep = task.next(res);
                return stepFoward();
            })
        } else {
            console.log('done')
            return Promise.resolve(lastStep.value)
        }
    }
    return stepFoward();
}    

//doAsync被转换成:
runGenerator(function* doAsyncFromGenerator() {
    console.log('start');
    yield doLazy1();
    console.log('after Lazy1');
    yield doLazy2();
    console.log('after Lazy2');
    const res = yield doNow();
    console.log('after now');
    return res;
})
// doAsync();
//打印出:
// start
// do Lazy1
// after Lazy1
// do Lazy2
// after Lazy2
// do now!
// after now
// 表达式输出为
// "now"

//babel插件思路
function transportAsyncFunction({ types }) {
    return {
        visitor: {
            FunctionDeclaration(path) {
                if (path.node.async === true) {
                    path.node.async === false;
                    path.node.generator === true;
                }
                //遍历函数内容
                path.traverse({AwaitExpression(pathAwait){
                    //替换AwaitExpression节点为YieldExpression
                    pathAwait.node.type = 'YieldExpression';
                }})
                //把节点转换为CallExpression
                //创建runGenerator 把节点转换为CallExpression并把上一步得到的节点当作param
                //替换上一步得到的节点
                types.replaceWith()
            }
        }
    }
}

//Function构造函数思路
fucntion(target){
    //匹配async关键字
    const regAsync = //s*async[^{}\(]{1,}(?=function|\()/;
    //匹配await关键字
    const regAwait = /(?<=[/s]+)(await)(?=[/s]+)/g;
    const regBody = /(?<!{){(.*)}(?!})/;
    let funcStr = target.toString();
    let resultStr = null;
    resultStr = funcStr.replace(regAsync,' * ');
    //如果是箭头函数需要转换成普通函数
    resultStr = funcStr.replace(/\(([/w]+)\)[/s]*=>/,'function($0)');
    //提取参数
    const params = funcStr.replace(/\(([/w]*)\)/,'$0').split(',');
    resultStr = funcStr.replace(regAwait,' yield ');
    const body = resultStr.replace(regBody,'$1');
    //构造出函数
    const resultFunction = new Function(...params,body);
    return runGenerator(resultFunction());
}

点赞
收藏
评论区
推荐文章
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
1年前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
郜小超 郜小超
4年前
用 async/await 来处理异步
一级标题昨天看了一篇vue的教程,作者用async/await来发送异步请求,从服务端获取数据,代码很简洁,同时async/await已经被标准化,是时候学习一下了。先说一下async的用法,它作为一个关键字放到函数前面,用于表示函数是一个异步函数,因为async就是异步的意思,异步函数也就意味着该函数的执行不会阻塞后面代码的执行。写一个async
Souleigh ✨ Souleigh ✨
4年前
理解 Javascript 中的 Async / Await
在本文中,我们将探讨async/await,对于每个Javascript开发人员来说,是异步编程的首选工具。如果您不熟悉javascript,请不要担心,本文将帮助您async/await从头开始理解。介绍async/await是javascript中的一种模式,可使您的代码以同步方式执行,但又不影响javascript的异步行为。定义异步功能要定义一
Karen110 Karen110
4年前
盘点JavaScript中async/await知识
大家好,我是进阶学习者。一、前言Async/await是以更舒适的方式使用promise的一种特殊语法,同时它也非常易于理解和使用。二、Asyncfunction让以async这个关键字开始。它可以被放置在一个函数前面。如下所示:asyncfunctionf()return1;在函数前面的“async”这个单词表达了一个简单的
Chase620 Chase620
4年前
js异步的5种样式
js异步的5种样式1.定时器2.AJAX3.Promise4.Generator5.asyns和awite 1.定时器  setTimeout():延时器    可以传入三个分别是      1)code:必
Chase620 Chase620
4年前
Promise从入门到拿Offer之手写Promise
1、Promise构造函数的实现Promise构造函数用来声明示例对象,需要传入一个执行器函数。其中包括resolve函数和reject函数,以及几个重要的属性:状态属性、结果属性和回调函数队列。构造函数的基本框架resolve函数用于异步处理成功后调用的函数。其中包括验证对象状态修改次数,修改promise实例对象状态,异步调用成功的回调函数
Stella981 Stella981
4年前
JavaScript回调函数的高手指南
摘要:本文将会解释回调函数的概念,同时帮你区分两种回调:同步和异步。回调函数是每个前端程序员都应该知道的概念之一。回调可用于数组、计时器函数、promise、事件处理中。本文将会解释回调函数的概念,同时帮你区分两种回调:同步和异步。1.回调函数首先写一个向人打招呼的函数。只需要创建一个接受name参数的函数gree
Stella981 Stella981
4年前
ES6 Promise 对象扯谈
newPromise(/executor/function(resolve,reject){...});Promise的构造函数接收一个函数作为参数,函数里面传入两个参数:resolve,reject,分别表示异步操作执行成功后的回调函数和异步操作执行失败后的回调函数。其实这里用“成功”和“失败”来描述并不准确,按照标准来
Wesley13 Wesley13
4年前
MongoDB 聚合函数
概念聚合函数是对一组值执行计算并返回单一的值主要的聚合函数countdistinctGroupMapReduce1、countdb.users.count()db.users.count({"uname":"hxf1"})db.users.count({"salary":{"$gt":15000}
小万哥 小万哥
1年前
资源描述框架的用途及实际应用解析
RDF(资源描述框架)是一种用于机器理解网络资源的框架,使用XML编写。它通过URI标识资源,用属性描述资源,便于计算机应用程序处理信息。RDF在语义网上促进信息的确切含义和自动处理,使得网络信息可被整合。RDF语句由资源、属性和属性值组成。RDF文档包括&lt;rdf:RDF&gt;根元素和&lt;rdf:Description&gt;元素,后者用about属性标识资源。RDF还支持容器(如&lt;Bag&gt;、&lt;Seq&gt;和&lt;Alt&gt;)来描述集合。RDFS是RDF的扩展,提供描述类和属性的框架,而达布林核心是一组预定义属性,用于描述文档。