Js Promise async await Generator


前言

Promise async generatorES6之后才被提出来的,他们都能够用来解决以前JS异步调用产生的一系列问题,例如大名鼎鼎的回调地狱!!!
什么是回调地狱?
在以前js中,我们是无法知晓一个异步操作是否执行完成,为了在异步操作完成后执行特定的代码,我们需要传入回调函数,请看下面的栗子:
这是一个简单的例子,在请求完成后(可以理解为异步操作)执行特定的代码

    //我们需要在请求完成后输出请求完成,请看回调法
    function show(params) {
        request('这是请求参数', () => {
            console.log('请求完成')
        })
    }
    /**
     * 模拟发起一个http请求
     * @param {object} data 请求的参数
     * @param {function} callBack 回调函数
     */
    function request(data, callBack) {
        //下面的定时器模拟请求时间
        setTimeout(data => {
            callBack(data);
        }, 3000);
    }
    show()

一次回调当然简单,如果是在这次请求完成后需要立即发起下一次请求呢?例如需要请求request10次,必须在上次请求完成后才能进行下一次请求,来看看 回调地狱 是怎么样的

    //我们需要在请求完成后输出请求完成,请看回调法
    function show(params) {
        request('这是请求参数', () => {
            console.log('请求完成1次')
            request('这是请求参数', () => {
                console.log('请求完成2次')
                request('这是请求参数', () => {
                    console.log('请求完成3次')
                    request('这是请求参数', () => {
                        console.log('请求完成4次')
                        request('这是请求参数', () => {
                            console.log('请求完成5次')
                            //这才第五次.....
                        })
                    })
                })
            })
        })
    }
    /**
     * 模拟发起一个http请求
     * @param {object} data 请求的参数
     * @param {function} callBack 回调函数
     */
    function request(data, callBack) {
        //下面的定时器模拟请求时间
        setTimeout(data => {
            callBack(data);
        },1000);
    }
    show()

这才第5次回调,但是代码的可读性已经极差了!
让我们先看看 Promise async generator怎么解决这个问题,后面再说其使用方式


首先 Promise

   //我们需要在请求完成后输出请求完成,请看回调法
   function show(params) {
       request('这是请求参数').then(
           resolve => {
               console.log('请求完成1次');
               return request('这是请求参数')
           }
       ).then(
           resolve => {
               console.log('请求完成2次');
               return request('这是请求参数')
           }
       ).then(
           resolve => {
               console.log('请求完成3次');
               return request('这是请求参数')
           }
       ).then(
           resolve => {
               console.log('请求完成4次');
               return request('这是请求参数')
           }
       ).then(
           resolve => {
               console.log('请求完成5次');
               return request('这是请求参数')
           }
       )
   }
   /**
    * 模拟发起一个http请求
    * @param {object} data 请求的参数
    * @param {function} callBack 回调函数
    */
   function request(data) {
       return new Promise(
           resolve => {
               //下面的定时器模拟请求时间
               setTimeout(data => {
                   resolve(data)
               }, 1000);
           }
       )
   }
   show()

虽然还是很长,但是至少嵌套很少了,可读性也比之前更高


再来看看 async
切记,async必须和Promise配合使用

    //我们需要在请求完成后输出请求完成,请看回调法
    async function show(params) {
        let result = await request('这是请求参数')
        console.log('请求完成1次');
        result = await request('这是请求参数')
        console.log('请求完成2次');
        result = await request('这是请求参数')
        console.log('请求完成3次');
        result = await request('这是请求参数')
        console.log('请求完成4次');
        result = await request('这是请求参数')
        console.log('请求完成5次');
    }
    /**
     * 模拟发起一个http请求
     * @param {object} data 请求的参数
     * @param {function} callBack 回调函数
     */
    function request(data) {
        return new Promise(
            resolve => {
                //下面的定时器模拟请求时间
                setTimeout(data => {
                    resolve(data)
                }, 1000);
            }
        )
    }
    show()

代码是不是更加简短了?而且看起来和同步一样,事实上,这就是使用同步的方式写异步代码,这代码也是同步执行的


最后看看 generator

    //我们需要在请求完成后输出请求完成,请看回调法
    function* show() {
        let a1 = yield request('请求参数', () => {
            console.log('这是第1次调用');
        });
        let a2 = yield request('请求参数', () => {
            console.log('这是第2次调用');
        });
        let a3 = yield request('请求参数', () => {
            console.log('这是第3次调用');
        });
        let a4 = yield request('请求参数', () => {
            console.log('这是第4次调用');
        });
        let a5 = yield request('请求参数', () => {
            console.log('这是第5次调用');
        });
    }
    /**
     * 模拟发起一个http请求
     * @param {object} data 请求的参数
     * @param {function} callBack 回调函数
     */
    function request(data, callBack) {
        //下面的定时器模拟请求时间
        setTimeout(() => {
            callBack(data)
        }, 1000);
    }
    let a = show()
    a.next();
    a.next();
    a.next();
    a.next();
    a.next();

以上是异步编程的ES6解决方案,接下来让我们把这3种方式都详细了解下


一.Promise

关于Promise的一些原型,函数,请移步 官方链接
Promise的中文名,也就是承诺,保证,
大家可以将Promise理解为JS的一个承诺,也就是对异步操作的一个承诺,咱先不管异步操作是否能够执行成功,使用Promise的所有异步操作,JS已经承诺处理了,咱就通过Promise的状态来知晓异步操作的执行结果。
一个 Promise有以下几种状态:

  • pending: 初始状态,既不是成功,也不是失败状态。
  • fulfilled: 表示着操作完成,状态成功。
  • rejected: 意味着操作失败。
    pending 状态的 Promise 对象可能会变为fulfilled 状态并传递一个值给相应的状态处理方法,也可能变为失败状态(rejected)并传递失败信息。当其中任一种情况出现时,Promise 对象的 then 方法绑定的处理方法(handlers )就会被调用
    上文提到Promise的原型中的函数thenthen可以接收2个参数(resolve [,reject])
    第一个参数resolve 是对成功的一个处理,类型为Function。你可以在其中做 一些异步成功后的操作
    第二个参数reject是对失败的一个处理,类型为Function。你可以在其中做 一些异步失败后的操作
    一般情况下then我们只会传一个参数,也就是默认的成功处理,失败处理会使用 catch函数
    catch函数只有一个参数,也就是代表失败的reject
    来看看then catch的使用案例
    function show(params) {
        //正常的请求成功操作
        request('这是请求参数').then(
            resolve => {
                console.log(resolve);
            }
        )
        //在then里面同时做成功和失败的操作
        request('这是请求参数').then(
            resolve => {
                //这儿是成功
                console.log(resolve);
            },
            reject => {
                //这儿是失败
                console.log(reject);
            }
        )
        //正常的请求失败操作
        request('这是请求参数').catch(
            reject => {
                console.log(reject);
            }
        )
    }
    /**
     * 模拟发起一个http请求
     * @param {object} data 请求的参数
     * @param {function} callBack 回调函数
     */
    function request(data) {
        return new Promise(
            (resolve, reject) => {
                //下面的定时器模拟请求时间
                setTimeout(data => {
                    // resolve('请求成功')
                    reject('请求失败')
                }, 1000);
            }
        )
    }
    show()

使用 Promise 能够使你的异步操作变得更加优雅,可读性也比较高,同时,Promise在ES6的各大插件中也使用的相当广泛,所以掌握 Promise是非常有必要的


二.async / await

想详细了解更多,请移步官方文档
async关键字用来定义一个function,用来标识此函数是一个异步函数
切记 切记 切记, await 关键字仅仅在 async function中有效。如果在 async function函数体外使用 await ,你只会得到一个语法错误SyntaxError

async关键字放在函数的声明之前,例如:

    async function test() {
    }
    async () => {

    }
    async data => {

    }
    class Test {
        async show() {
            
        }
    }

无论是普通的函数,Lambda表达式,或是ES6的class,该关键字都是放置在函数的声明之前
在调用声明了async的函数时,会返回一个Promise对象
await关键字则是放置在异步操作的调用之前,await会使得async函数在执行到异步操作时暂停代码执行,直到异步操作返回的Promise状态更改为 fulfilledrejected,此时代码会继续执行,并自动解析Promise返回的值,无论是成功还是失败,都会解析到await关键字前面定义接收的参数,请看例子:

class Test {
    /**
     * 线程休眠
     * @param {number} timer 休眠毫秒数
     */
    Sleep(timer) {
        return new Promise(
            resolve => {
                setTimeout(() => {
                    resolve(timer)
                }, timer);
            }
        )
    }
}
let T = new Test();
async function show() {
    console.log('第一次');
    await T.Sleep(1000)
    console.log('第二次');
}
show()

上面的实例调用show函数,会立即打印出 第一次,延时1000毫秒后,会打印出 第二次
原理嘛,就是模仿Java的线程休眠函数Sleep
在打印了 第一次 后,会调用T的Sleep函数,Sleep函数返回了一个Promise,在定时器执行完毕后调用Promiseresolve函数,会将Promise 的状态更改为 fulfilled,此时await检测到Promise的状态更改,继续执行代码,输出 第二次


三 . generator

generator(生成器)是ES6标准引入的新的数据类型,使用方式是在函数名前加上*,generator和普通的函数差不多,但是可以返回多次。
嗯,所有的函数都有默认的返回值,如果没有明确定义,那就会返回undefined,generator也不例外!
generator使用yield关键字来中途返回值,请看案例

    function* a(num) {
        let sum = yield num + 1
        console.log(sum);//2 此处是next(r.value)传入的值
        let sum2 = yield sum + 2
    }
    let result = a(1);
    let r = result.next()
    console.log(r);//此处返回第一次yield的值 2
    console.log(result.next(2));//此处返回第二次yield的值 4
    console.log(result.next());//此处并没有第三次yield


第一次输出的是第一次yield的值,此时num为调用a函数时传入的值 1,yield返回了num+1,所以第一行打印的对象 value值是 2
第二次打印的是sum值,也就是第一个yield关键字前面接收的值,此值是下面result.next(2)传入的 ,next函数传入的参数,会赋值到相应的yield关键字左边接收的那个变量,在上方案例,也就是sum变量
第三次输出的是a函数中第二个yield返回的值,此时sum为第一次next(2)传入的2,所以此次返回的值是2+2,所以值也就是 4
第四次输出的是最后一个next(),但是上方generator并没有相应的yield返回,所以此时的valueundefined
yield返回的值是一个对象,其中有donevalue两个属性,

  • done 表示该generator是否执行完毕,当没有yield返回时,done的值为true,也就是代表当前generator执行完毕
  • value表示此次yield关键字右方表达式返回的值,当没有yield时,valueundefined

generator是支持迭代器操作的,例:

      function* a(num) {
        let sum = yield num + 1
        console.log(sum);//2 此处是next(r.value)传入的值
        let sum2 = yield sum + 2
    }
    let result = a(1);
    for (const key of result) {
        console.log(key);
        
    }


事实证明generator是实现了迭代器的接口的!
嗯,关于generator的实际应用场景,我是没有遇见的,不过听说 async/await是generator的语法糖??

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342

推荐阅读更多精彩内容