博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Generator与yield 语法糖 async和await
阅读量:3903 次
发布时间:2019-05-23

本文共 8160 字,大约阅读时间需要 27 分钟。

Generator与yield

Generator函数跟普通函数的写法区别:

一是,function关键字与函数名之间有一个星号;

二是,函数体内部使用yield语句,定义不同的内部状态(yield在英语里的意思就是“产出”)。

 简单的Generator函数如下:

// 通过定义Generator函数 来发送两次AJAX请求function * Gen() {    // 发送第一次ajax    $.ajax({        url: "/ajax1",        dataType: "json",        success: function(data) {            console.log("发送完毕ajax1");            g.next(); // 当第一次ajax请求结束之后,就发送第二次        }    });    yield;    $.ajax({        url: "/ajax2",        dataType: "json",        success: function(data) {            console.log("发送完毕ajax2");        }    });}// 调用let g = Gen();g.next(); // 发送了第一次ajax请求

 从Generator函数内部向外部返回数据

内部的传递方式:yield关键字后面可以跟内容。这些内容会被返回出去。

外部的接收方式:Gen.next() 会返回一个对象,对象中有value有done 其中value就是yield给返回出来的内容。

// 定义Generator函数function * g() {    yield 'a';    yield 'b';    yield 'c';    return 'ending';}// 调用var gen = g();gen.next(); // 返回Object {value: "a", done: false}

gen.next()返回一个非常非常简单的对象{value: "a", done: false},'a'就是g函数执行到第一个yield语句之后得到的值,false表示g函数还没有执行完,只是在这暂停。

如果再写一行代码,还是gen.next();,这时候返回的就是{value: "b", done: false},说明g函数运行到了第二个yield语句,返回的是该yield语句的返回值'b'。返回之后依然是暂停。

再写一行gen.next();返回{value: "c", done: false},再写一行gen.next();,返回{value: "ending", done: true},这样,整个g函数就运行完毕了。

所以现在可以看出,每次调用next方法,内部指针就从函数头部或上一次停下来的地方开始执行,直到遇到下一个yield语句(或return语句)为止。换言之,Generator函数是分段执行的,yield语句是暂停执行的标记,而next方法可以恢复执行。

总之,每调用一次Generator函数,就返回一个迭代器对象,代表Generator函数的内部指针。以后,每次调用迭代器对象的next方法,就会返回一个有着value和done两个属性的对象value属性表示当前的内部状态的值是yield语句后面那个表达式的值done属性是一个布尔值,表示是否遍历结束

所以可以看出,Generator 函数的特点就是:

1、分段执行,可以暂停

2、可以控制阶段和每个阶段的返回值
3、可以知道是否执行到结尾

yield语句

迭代器对象的next方法的运行逻辑如下。

(1)遇到yield语句,就暂停执行后面的操作,并将紧跟在yield后面的那个表达式的值,作为返回的对象的value属性值。

(2)下一次调用next方法时,再继续往下执行,直到遇到下一个yield语句。

(3)如果没有再遇到新的yield语句,就一直运行到函数结束,直到return语句为止,并将return语句后面的表达式的值,作为返回的对象的value属性值。

(4)如果该函数没有return语句,则返回的对象的value属性值为undefined。

yield语句与return语句既有相似之处,也有区别。

相似之处在于,都能返回紧跟在语句后面的那个表达式的值。

区别在于每次遇到yield,函数暂停执行,下一次再从该位置继续向后执行,而return语句不具备位置记忆的功能。一个函数里面,只能执行一次(或者说一个)return语句,但是可以执行多次(或者说多个)yield语句。正常函数只能返回一个值,因为只能执行一次return;Generator函数可以返回一系列的值,因为可以有任意多个yield。从另一个角度看,也可以说Generator生成了一系列的值,这也就是它的名称的来历(在英语中,generator这个词是“生成器”的意思)。

注意:yield语句只能用于function*的作用域,如果function*的内部还定义了其他的普通函数,则函数内部不允许使用yield语句。

for...of循环

for...of循环可以自动遍历Generator函数时生成的Iterator对象,且此时不再需要调用next方法。for...of循环的基本语法是:

//其中foo()是迭代器对象,可以把它赋值给变量,然后遍历这个变量。function* foo() {  yield 1;  yield 2;  yield 3;  yield 4;  yield 5;  return 6;}let a = foo();for (let v of a) {  console.log(v);}// 1 2 3 4 5

Generator.prototype.throw()

Generator函数返回的迭代器对象,都有一个throw方法,可以在函数体外抛出错误,然后在Generator函数体内捕获。

既然我的文章是简单理解Generator函数,所以错误捕获直接跳过。

Generator.prototype.return()

Generator函数返回的迭代器对象,还有一个return方法,可以返回给定的值,并且终结遍历Generator函数。

function* gen() {  yield 1;  yield 2;  yield 3;}var g = gen();console.log(g.next());        // { value: 1, done: false }console.log(g.return('foo')); // { value: "foo", done: true }console.log(g.next());        // {value: undefined, done: true}

就是说,return的参数值覆盖本次yield语句的返回值,并且提前终结遍历,即使后面还有yield语句也一律无视。

Co函数的使用  结合promise与Generator  优化代码

function co(gen) {     // 返回值是一个Promise    return new Promise(function(resolve, reject) {        // 在promise中执行Generator函数         const g = gen();        // 定义一个函数        function next(nextFun) {            // 预先定义变量            let f = null;            // 尝试执行            try {                f = nextFun();            } catch(e) {                // 如果有错误,则被捕获到的错误信息被传递到rejected回调函数中                return reject(e);            }            // 判定是否执行结束            if (f.done) {                // 如果已经结束,此时才会执行返回的Promise实例的成功时的回调函数                return resolve(f.value);            }            // 如果没有结束,就将这一次的返回结果强行制作成一个Promise实例            Promise.resolve(f.value).then(function(result) {                // 如果f.value是一个值 则result就是该值                // 如果f.value是一个Promise实例 则result是它成功时的传递的参数                // 如果f.value是一个具备then方法的对象 则result是这个对象的then方法执行完毕之后的成功时的返回值                // 不论如何,result一定是成功时才会有的                // 也即是说,这个函数一定是在成功时才会执行的                // 如果成功了,就调用next继续下一次的执行                next(function() {                    // 继续下一次的执行                    return g.next(result);                });            }, function(result) {                // 如果f.value是一个值,则此函数不会执行                // 如果f.value是一个Promise实例 则result是它失败时的传递的值                // 如果f.value是一个具备then方法的对象 则result是这个then方法执行失败时的传递的信息                // 不论如何,result一定是失败时才会有的                // 也即是说,这个函数一定是在失败的时候才会执行的                // 如果失败了,就直接终止函数的执行                next(function(result) {                    return g.throw(result);                });            })        }        // 开启第一次的next执行        next(function() {            return g.next(undefined);        });    })}

通过co函数来使用: 条件,yield之后跟的一定要是Promise实例。

//引入封装的co函数// 定义任务 function taskOne() {    return new Promise(function(resolve, reject) {        $.ajax({            url: "/ajax1",            dataType: "json",            success: function(data) {                resolve(123);            }        });    });}function taskTwo() {    return new Promise(function(resolve, reject) {        $.ajax({            url: "/ajax2",            dataType: "json",            success: function(data) {                resolve(data);            }        });    });}// 定义Generator函数function * Gen() {    let data = yield taskOne();    console.log(data);    let data1 = yield taskTwo();    console.log(data1);}co(Gen);

再看这种写法:

每一个任务中,都与其它任务不干涉。 很干净

 

async和await

async定义一个异步函数 它其实是Generator的语法糖

await定义一个等待函数 它其实是yield的语法糖

              async定义一个异步函数 它其实是Generator的语法糖

                               定义语法: async function fun() {}

                              当定义了一个async函数之后,此时,函数的性质就变了。

                               普通函数的返回值,是由函数内的return关键字决定。

                              async函数的返回值,总是返回Promise实例。

// 语法糖:指的是对高级复杂特性的一种简化使用方式async function taskOne() {    console.log(1);    await new Promise(function(resolve, reject) {        setTimeout(function() {            console.log("等待了3秒");            resolve();        }, 3000);    });    console.log(2);    await new Promise(function(resolve, reject) {        setTimeout(function() {            console.log("等待了3秒");            resolve();        }, 3000);    })    console.log(3);}// 执行这个异步函数taskOne();

async

async定义一个异步函数 它其实是Generator的语法糖

定义语法: async function fun() {}

当定义了一个async函数之后,此时,函数的性质就变了。

普通函数的返回值,是由函数内的return关键字决定。

async函数的返回值,总是返回Promise实例。

// 定义普通函数function fun() {}// 调用普通函数let f = fun();console.log(f); // undefined// 定义异步函数async function func() {    }let f1 = func();console.log(f1); // Promise的实例

它的状态其实是由异步函数内的执行情况来决定。

如果一切正常,该函数内的所有代码能够顺利执行完毕。则状态变为resolved

如果有异常,则状态变为rejected

它的结果是由异步函数的返回值决定

 

await

await后面的内容

如果是Promise实例。则await左侧的变量接收到的值,就是Promise实例resolve()的传参。

如果不是Promise实例。则会使用Promise.resolve将它转为Promise实例。

// 定义asyncasync function fun() {    let a = await 123;}

此时,没有任何异常。但是如果将async去掉

await表示“等待”。当执行到await的时候,会交出线程的控制权。

// 定义async函数async function fun() {    console.log(123);    await 123;    console.log(321);}fun();console.log("哈哈哈哈");

await后面跟的是Promise实例,Promise实例如果成功,则await左边的变量可以得到数据。得到的值就是resolve时的数据。

// 验证async function fun() {    let a = await new Promise(function(resolve, reject) {        resolve("hello world");     });    console.log(a);}fun();

注:resolve执行时传递的是一个字符串。所以await左边的变量a得到的就是一个字符串。

规则: resolve接收什么参数,await左边的变量就得到什么。

 

错误处理  catch      try  catch

如果代码中出现了错误,则会将异步函数返回值的Promise实例状态变为rejected并传递错误。

如果某一个await后面的Promise实例状态变为rejected。则也会将异步函数返回值的Promise实例状态变为rejected,并传递错误。

// 定义async函数async function fun() {    throw new Error("123"); // 这一条会报错    let a = await new Promise(function(resolve, reject) {        setTimeout(function() {            reject(123); // 这一条也会报错        }, 3000);    });}// 如果某一个await后面的Promise实例状态变为了rejected // 则直接中止函数的执行并将异步函数Promise实例的状态变为rejected let p = fun();p.catch(function(err) {    console.log(err);});

还有另外一种处理方式:

// 定义async函数async function fun() {    // throw new Error("123");    try {        let a = await new Promise(function(resolve, reject) {            setTimeout(function() {                reject(123);            }, 3000);        });    }catch(err) {        console.log(err);    }    // 后续代码……}// 如果某一个await后面的Promise实例状态变为了rejected // 则直接中止函数的执行并将异步函数Promise实例的状态变为rejected let p = fun();p.catch(function(err) {    console.log(err);});

因为这里是在异步函数内部处理了错误,所以异步函数中的后续代码部分会继续执行。但如果没有try……catch。则会影响到异步函数的返回值的Promise状态变化。会导致后续代码部分不会继续执行

转载地址:http://eoven.baihongyu.com/

你可能感兴趣的文章
申宝策略随评:资金对题材板块攻击态度已分散
查看>>
申宝配资策略:连续3天弱反后,明天冲高兑现减仓
查看>>
申宝分析短线情绪分歧比较大
查看>>
申宝商场仍是非常健康的走势
查看>>
申宝简述今天的盘面仍然不差
查看>>
申宝配资总结工作板块继续是涨多跌少
查看>>
申宝点评指数缩量震动
查看>>
申宝配资提示指数今日不看好
查看>>
申宝配资分析接力高位股强分歧
查看>>
申宝配资人气票盘前预判
查看>>
【推荐】J2ee学习流程
查看>>
JSplitPane详解
查看>>
JAVA自学之路(很有道理,很深刻,值得一读)
查看>>
用JAVA写的破解MYECLIPSE的程序,真是牛逼
查看>>
JTree用法详解
查看>>
Iframe用法浅析
查看>>
Web性能测试基本性能指标
查看>>
影响Hibernate性能的因素
查看>>
WML的基本情况概述
查看>>
java培训笔记一
查看>>