zoukankan      html  css  js  c++  java
  • javascript处理异步的三种方法

    一、ES6 Promise对象

    const result = new Promise((resolve, reject) => {
         if (success) {
              resolve('成功');
         } else {
              reject('失败');
         }
     });
     result.then((res) => {
         console.log(res); //输出成功
     });
     result.catch((res) => {
         console.log(res); //输出失败
     });
     
    常用API:
         1.resolve 返回异步操作成功的结果
         2.reject 返回异步操作失败的结果
         3.then 执行Promise状态是成功的操作
         4.catch 执行Promise状态是失败的操作
         5.finally 不管Promise状态是成功或失败都执行的操作
     
    Promise.all方法简介:
         Promse.all在处理多个异步处理时非常有用,比如说一个页面上需要等两个或多个ajax的数据回来以后才正常显示
    const p = Promise.all([p1, p2, p3])
    //顺序执行p1,p2,p3,即使p2的执行时间较短仍要等待p1执行完成
    //p1,p2,p3为promise对象
    p.then((result) => {
      console.log(result)      //result 是个数组,按promise的顺序依次返回结果
    }).catch((error) => {
      console.log(error)
    })
    p的状态由p1、p2、p3决定,分成两种情况。 
    1)只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。
    2)只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。
     

    二、ES6 Generator 函数

           Generator函数是ES6引入的新型函数,用于异步编程,跟Promise对象联合使用的话会极大降低异步编程的编写难度和阅读难度。

           Generator函数跟普通函数的写法有非常大的区别:

            一是,function关键字与函数名之间有一个星号;
            二是,函数体内部使用yield语句,定义不同的内部状态(yield在英语里的意思就是“产出”)。

            

    function* g() {
        yield 'a';
        yield 'b';
        yield 'c';
        return 'ending';
    }
    g(); // 返回一个对象
    g().next(); // 返回Object {value: "a", done: false}
    g().next(); // 返回Object {value: "b", done: false}
    g().next(); // 返回Object {value: "c", done: false}
    g().next(); // 返回Object {value: "ending", done: false}
    g().next(); // 返回Object {value: "undefined", done: false}

    generator函数中next()参数:

    next方法参数的作用,是为上一个yield语句赋值。
    由于yield永远返回undefined(执行yield语句后就阻止了 例如:let y = yield '55',y返回undefined,下一个next方法中仍然是undefined)
    这时候,如果有了next方法的参数,yield就被赋了值,当下一个next方法传入值后,y就被赋予了新值。
    带参数跟不带参数的区别是,带参数的情况,首先第一步就是将上一个yield语句重置为参数值,然后再照常执行剩下的语句。总之,区别就是先有一步先重置值,接下来其他全都一样。
    此带参数的方法可以用在多接口并发的处理上

    generator函数在vue中的应用:

      <button @click="()=>this.testM.next()">generator</button>
      mounted(){
        this.testM=this.go()     //每次调用this.go().next()都会把原先的销毁,所以需要先保存起来,会导致每次点击都只会触发第一个yield
      }
      
      *go() {
         while (true) {          //实现一直循环,否则当点击二次后就无法继续执行了
         console.log('Tick!');
         yield;
         console.log('Tock!');
         yield;
        }
      },
     
      //此示例 等同于下面的写法
    var ticking = true;
    var go= function() {
      if (ticking)
        console.log('Tick!');
      else
        console.log('Tock!');
      ticking = !ticking;
    }

    相比的优点
    Generator 函数实现的状态机不用设初始变量,不用切换状态,上面的Generator函数实现与ES5实现对比,
    可以看到少了用来保存状态的外部变量ticking,这样就更简洁,更安全(状态不会被非法篡改)、更符合函数式编程的思想,在写法上也更优雅。


    Generator+Promise实现完美异步

    //接口异步发生,互不阻塞。时间消耗为2s。

    var
    resolveAfter1Second = function() { return new Promise(resolve => { setTimeout(function() { resolve("fast"); console.log("fast promise is done"); }, 1000); }); }; var resolveAfter2Seconds = function() { return new Promise(resolve => { setTimeout(function() { resolve("slow"); console.log("slow promise is done"); }, 2000); }); }; function *dealData() { let p1 = resolveAfter1Second(); // 请求1获取到 r1 let p2 = resolveAfter2Seconds(); // 请求2获取到 r2
    //此时2个延时操作已经异步执行了,会先后打出console的内容,但是操作结果resolve并没有处理(并发执行操作,减少耗时)
    let r1 = yield p1; //resolve的结果在这处理
    let r2 = yield p2; //resolve的结果在这处理 }
    var gen=dealData(); //此时"fast promise is done" 和 "fast 会在1秒后输出 var a=gen.next().value; //此时已执行一次generator函数,‘fast promise is done’已经被打出 a.then(function(res){ console.log(res) //打出resolve的结果'fast' ,由于操作早已经执行,这里的结果会和‘fast promise’同时打出,不存在延时 var b=gen.next().value.then(function(res){ //此时slot 和 slow promise is done 会在fast延迟后1秒输出 console.log(res) }) })

    //如果想要2个接口同步顺序发生,则:(耗时3秒)
    function *dealData() {
        yield resolveAfter1Second();   
        yield resolveAfter2Seconds();
     }
    
    

     三、async

          以上yield + Promise的写法需要我们对拿到的promise的决议进行人工处理(区分成功或失败)           //    .then()操作

          在ES7中提供了async/await帮我们省掉了这个步骤:async/await组合的出现使得异步的世界更加完美啦~~

    //使用async对上面的genrator+promise方法的进行优化,总耗时为2s

    var
    resolveAfter2Seconds = function() { return new Promise(resolve => { setTimeout(function() { resolve("slow"); console.log("slow promise is done"); }, 2000); }); }; var resolveAfter1Second = function() { return new Promise(resolve => { setTimeout(function() { resolve("fast"); console.log("fast promise is done"); }, 1000); }); }; var concurrentStart = async function() { const slow =resolveAfter2Seconds(); // starts timer immediately const fast = resolveAfter1Second(); // starts timer immediately
      //此时2个延时操作已经异步执行了,会先后打出console的内容,但是操作结果resolve并没有处理(并发执行操作,减少耗时)
      console.log(await fast); // fast的延时操作已经执行完毕,操作结果会立刻输出
      console.log(await  slow); // slow的延时操作会慢1s
      
    }
    concurrentStart()

    //如果想要同步顺序执行,则:
    var concurrentStart = async function() {
      const slow =await resolveAfter2Seconds(); // await会阻塞下一个接口的执行
      const fast =await resolveAfter1Second(); // 等待上一个awit执行完毕
      console.log(fast); // fast的延时操作已经执行完毕,操作结果会立刻输出
      console.log(slow); // slow的延时操作会慢1s
      
    }
     

    那么async/await的写法和yield相比孰优孰劣呢?
    其实两者都有自己独到的长处

    • async/await在处理promise的层面上省略了对决议的人工处理,让代码量得以减少,语义上也更容易理解。    //async 在异步接口处理上更加简便
    • yield包容性更广泛,async只能接口promise,yield除此之外还能接收字符串、数组、对象等各种类型的数据。   //yield可以当成一个状态机使用
  • 相关阅读:
    Web前端开发——HTML概述
    Web前端开发——概述
    [nowCoder] 两个不等长数组求第K大数
    [nowCoder] 两个长度相同有序数组的中位数
    [nowCoder] 完全二叉树结点数
    [nowCoder] 子数组最大乘积
    [nowCoder] 局部最小值位置
    [LeetCode] Binary Tree Right Side View
    [C/CPP系列知识] Type difference of character literals 和 bool in C and C++
    [C/CPP系列知识] C++中extern “C” name mangling -- Name Mangling and extern “C” in C++
  • 原文地址:https://www.cnblogs.com/8080zh/p/12620690.html
Copyright © 2011-2022 走看看