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可以当成一个状态机使用

 

 

posted on 2020-04-02 16:24  晓风零乱  阅读(1511)  评论(0编辑  收藏  举报

导航