JavaScript异步

时间:2019-08-30 23:28:04   收藏:0   阅读:68

 JavaScript异步类型

JavaScript常用异步编程

Promise

  Promise有几个特点:

  1. 对象的状态不受外界影响,有三种状态:pending(进行中)、fulfilled(成功)、rejected(失败)。只有异步操作的结果可以决定当前是哪种状态,其他操作无法改变。
  2. 状态一旦改变,就不会再变,任何时候都可以得到这个结果。状态改变只可能是:pending -> fulfilled 或 pending -> rejected
  3. 实例化后,会立即执行一次。所以一般将其用函数包裹起来,使用的时候调用一次。
  4. 如果执行后的回调也要做一些异步操作,可以无限的.then下去,当然要保证有返回值

  方法:

function promiseTest(n,msg) {
   return new Promise((resolve,reject)=>{
      setTimeout(function () {
         console.log(`执行第${n}个任务`);
         msg.code && resolve(msg.text);  // 当认为成功的时候,调用resolve函数
         !msg.code && reject(msg.text);   // 当认为失败的时候,调用reject函数
      },n*500)
   });
}
let pro = promiseTest(1,{code:true,text:"返回的数据1"});
/* 没有catch,每个then里两个回调函数,此时第一个为成功的回调,第二个为失败的回调 */
pro.then((data)=>{
   console.log(data); // 执行成功结果在这里
   // return promiseTest(2,{code:true,text:"返回的数据2"});
   return promiseTest(2,{code:false,text:"失败的数据"});
},(err)=>{
   console.log(err); // 执行失败的结果在这里
}).then((data)=>{console.log(data)},(err)=>{console.log(err)});

观察then和catch的用法:

/* 多个then和一个catch */
pro.then((data)=>{
   console.log(data);
   return promiseTest(2,{code:false,text:"失败的数据"});
}).then((data)=>{
   console.log(data)
}).catch((err,data)=>{
   console.log("失败了",err);
});

all和rece的用法:

/* all的用法 */
Promise.all([
   promiseTest(1,{code:true,text:"返回的数据1"}),
   promiseTest(2,{code:false,text:"返回的数据2"}),
   promiseTest(3,{code:false,text:"返回的数据3"})
]).then((res)=>{console.log("全部成功",res)}).catch((err)=>{console.log("失败",err);});

/* race的用法 */
Promise.race([
   promiseTest(1,{code:false,text:"返回的数据1"}),
   promiseTest(2,{code:false,text:"返回的数据2"}),
   promiseTest(3,{code:true,text:"返回的数据3"})
]).then((res)=>{console.log("成功",res)}).catch((err)=>{console.log("失败",err);});

Generator

Generator是什么?叫做生成器,通过function*关键字来定义的函数称之为生成器函数(generator function),与Python的语法一模一样

生成器有3个方法,都有一样的返回值{value,done}

生成器的作用:

可以和 Promise 组合使用。减少代码量,写起来更方便。在没有Generator时,写Promise会需要很多的then,每个then内都有不同的处理逻辑。现在,我们将所有的逻辑写进一个生成器函数(或者在生成器函数内用yield 进行函数调用),Promise的每个then内调用同一个函数即可。

定义生成器:

function add(a,b) {
   console.log("+");
   return a+b;
}
function cut(a,b) {
   console.log("-");
   return a-b;
}
function mul(a,b) {
   console.log("*");
   return a*b;
}
function division(a,b) {
   console.log("/");
   return a/b;
}
function* compute(a, b) {
  yield add(a,b);
  yield cut(a,b);
  let value = yield mul(a,b);
  console.log("value",value); // 第三次调用.next()时无法为value赋值,需要第四次调用才能为其赋值
  yield mul(a,b);
  yield division(a,b);
}

使用生成器:

// 执行一下这个函数得到 Generator 实例,调用next()方法执行,遇到yield暂停
let generator = compute(4, 2);

function promise() {
   return new Promise((resolve, reject) => {
      let res = generator.next();
      if(res.value > 5)
      {
         resolve("OK");
      }else
      {
         reject("小于5")
      }
   });
}

let proObj = promise();
proObj.then((data)=>{
   console.log(data);
   let res = generator.next();
   console.log("Promise res1",res);
}).then((data)=>{
   let res = generator.next();
   // let res = generator.return();
   console.log("Promise res2",res);
}).then((data)=>{
   let res = generator.next("qwe"); // 第四次next()时,向生成器传数据
   console.log("Promise res3",res)
}).catch((err)=>{
   console.log("出错",err);
});

async/await

优点:简洁,节约了不少代码

如果在异步函数中,每个任务都需要上个任务的返回结果,可以这么做:

function takeLongTime(n) {
   return new Promise((resolve,reject) => {
      setTimeout(() => {resolve(n + 200)}, n);
   });
}

function step1(n) {
   console.log(`step1 with ${n}`);
   return takeLongTime(n);
}

function step2(m, n) {
   console.log(`step2 with ${m} and ${n}`);
   return takeLongTime(m + n);
}

function step3(k, m, n) {
   console.log(`step3 with ${k}, ${m} and ${n}`);
   return takeLongTime(k + m + n);
}


async function doIt() {
    console.time("doIt");
    const time1 = 300;
    const time2 = await step1(time1);
    const time3 = await step2(time1, time2);
    const result = await step3(time1, time2, time3);
    console.log(`result is ${result}`);
    console.timeEnd("doIt");
}
doIt();

如果这几个任务没有关联,可以这样做:

async function doIt() {  // 函数执行耗时2100ms
   console.time("doIt");
   await step1(300).catch((err)=>{console.log(err)});  // 异常处理
   await step1(800);
   await step1(1000);
   console.timeEnd("doIt");
}
doIt();

当然,最好这样做:

async function doIt() { // 函数执行耗时1000ms
   console.time("doIt");
   const time1Pro = step1(300);
   const time2Pro = step1(800);
   const time3Pro = step1(1000);
   await time1Pro;
   await time2Pro;
   await time3Pro;
   console.timeEnd("doIt");
}
doIt();

注意:

  1. async/await并没有脱离Promise,它的出现能够更好地协同Promise工作。
    • 怎么体现更好地协同?它替代了then catch的写法。使得等待promise值的操作更优雅,更容易阅读和书写。
  2. 函数仅仅加上async并没有意义,它仍然是同步函数,只有与await结合使用,它才会变成异步函数。
    • 这需要精准理解await。它在等待的时候并没有阻塞程序,此函数也不占用CPU资源,使得整个函数做到了异步执行。
  3. doIt()函数内部是串行执行的,但它本身是异步函数。
  4. 在这个异步函数内,可能会做很多操作ABC,他们有执行的先后顺序。这时你可能会想,A、B、C之间没有关联,他们之间可以是并行执行的,并不需要串行,那怎么办?
    • 【错误想法】这样想没错,但是没必要。因为他们已经存在于异步函数内了,所有的操作已经是异步的。在同样的环境情景下,底层执行的效率是相同的,并不见得因为A和B之间互相异步而提高效率。
    • 【正确想法】这样想是有必要的。参照两个doIt(),调用的函数返回promise对象,前者是依次生成promise对象(依次执行任务),依次等待返回结果。等待总时长取决于所有任务执行时间之和。后者则是同时生成promise对象(同时执行任务),依次等待。等待总时长取决于耗时最长的任务。后者的CPU运用率更高。
  5. 错误处理。最标准的方法是使用try...catch语句,但是它不仅会捕捉到promise的异常,还会将所有出现的异常捕获。因此,可以使用.catch,只会捕获promise相关的异常。

 

原文:https://www.cnblogs.com/V587Chinese/p/11437051.html

评论(0
© 2014 bubuko.com 版权所有 - 联系我们:wmxa8@hotmail.com
打开技术之扣,分享程序人生!