• node 异步编程


    node 异步编程

    我了解到的node异步编程可分成: 
    1.回调函数 
    2.pub/sub模式(发布/订阅模式) 
    3.promise 
    4.generator 
    5.async await

    一.直接回调函数

    该方法是最直接常用的异步操作方式,比如在setInterval 和 ajax等会使用到, 存在缺点有: 
    1.代码不易阅读并且容易出现金字塔嵌套问题; 
    2.一般只能对应一个回调函数(一对一),使用上有一定的限制;

    fs.readFile('/etc/passwd', function (err, data) {
      if (err) throw err;
      console.log(data);
    });
    

    函数先读取/etc/passwd,再执行回调函数,在这两者之间抛出的错误会以参数形式传入回调函数中。

    二.发布/订阅模式

    该方法不再局限于一对一的形式,以多对多的形式监听事件,可以很方便的订阅和取消订阅存在缺点有:需要借助类库(jQuery),事件与回调函数的顺序很重要。

    三. Promise

    回调函数的缺点之一是容易出现函数多层嵌套,难以维护的场面.而es6语法中的promise正好解决这类问题. Promise包含三种状态:pending、fulfilled、rejected,三种状态只能发生两种转换(从pending—>fulfilled、pending—>rejected),并且状态的转换仅能发生一次。 
    Promise实例生成以后,可以用then方法分别指定Resolved状态和Reject状态的回调函数: 
    then方法可以接受两个回调函数作为参数。第一个回调函数是Promise对象的状态变为Resolved时调用,第二个回调函数是Promise对象的状态变为Reject时调用。 
    a) then方法返回Promise。这样就实现了多个异步操作的串行操作。 
    b)实现了多个不同异步库之间的转换。

    var p1 = new Promise(function (resolve, reject) {
      setTimeout(() => reject(new Error('fail')), 3000)
    })
    var p2 = new Promise(function (resolve, reject) {
      setTimeout(() => resolve(p1), 1000)
    })
    p2.then(result => console.log(result))
    p2.catch(error => console.log(error))
    // Error: fail
    Promise.all方法用于将多个Promise实例,包装成一个新的Promise实例。是我们项目中正在使用的方式。如var p = Promise.all([p1,p2,p3]);p的状态由p1、p2、p3决定,分成两种情况。

    (1)只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数。

    (2)只要p1、p2、p3之中有一个被rejected,p的状态就变成rejected,此时第一个被reject的实例的返回值,会传递给p的回调函数。

    四.Generator

    Generator最大的特点就是可以实现函数的暂停、重启,这个特性非常有利于解决异步操作有两个名词需要注意yield 和 next。

    1. generator的解决机制有点像线程.next方法的作用是分阶段执行Generator函数。每次调用next方法,会返回一个对象,表示当前阶段的信息(value属性和done属性)。value属性是yield语句后面表达式的值,表示当前阶段的值;done属性是一个布尔值,表示Generator函数是否执行完毕,即是否还有下一个阶段。
    2. yield 命令是标志异步操作的标志,如果去掉此命令,函数就会像执行同步函数那样执行就暂停,等到执行权返回,再从暂停的地方继续往后执行。 
      Generator 函数的还提供了数据交换和错误处理机制。其函数内部还可以部署错误处理代码,捕获函数体外抛出的错误。
    function* gen(x){
      try {
        var y = yield x + 2;
      } catch (e){ 
        console.log(e);
      }
      return y;
    }
    
    var g = gen(1);
    g.next();
    g.throw('出错了');
    // 出错了
    除此之外,generator函数还有两个小帮手Thunk函数(自动控制Generator函数的流程,接收和交还程序的执行权的一种机制)和co模块(用于Generator函数的自动执行。)

    五.async await

    ES7提供了async函数,成为generator函数的语法糖,它主要就是用async来说代替*,用await来代替yield,除此之外,他还带来了一些便利之处: 
    1.generator函数的调用需要借助next方法或者是co模块,而async和普通函数的调用一样,不需要借助任何函数; 
    2.co模块约定,yield命令后面只能是Thunk函数或Promise对象,而async函数的await命令后面,可以是Promise对象和原始类型的值 
    注意的是: await 命令只能用在 async 函数之中,如果用在普通函数,就会报错。

    async function dbFuc(db) {
      let docs = [{}, {}, {}];
    
      // 报错
      docs.forEach(function (doc) {
        await db.post(doc);
      });
    }

    上面代码会报错,因为 await 用在普通函数之中了。但是,如果将 forEach 方法的参数改成 async 函数,也有问题。

    async function dbFuc(db) {
      let docs = [{}, {}, {}];
    
      // 可能得到错误结果
      docs.forEach(async function (doc) {
        await db.post(doc);
      });
    }
    上面代码可能不会正常工作,原因是这时三个 db.post 操作将是并发执行,也就是同时执行,而不是继发执行。正确的写法是采用 for 循环。
    async function dbFuc(db) {
      let docs = [{}, {}, {}];
    
      for (let doc of docs) {
        await db.post(doc);
      }
    }

    如果确实希望多个请求并发执行,可以使用 Promise.all 方法。

    async function dbFuc(db) {
      let docs = [{}, {}, {}];
      let promises = docs.map((doc) => db.post(doc));
    
      let results = await Promise.all(promises);
      console.log(results);
    }
    
    // 或者使用下面的写法
    
    async function dbFuc(db) {
      let docs = [{}, {}, {}];
      let promises = docs.map((doc) => db.post(doc));
    
      let results = [];
      for (let promise of promises) {
        results.push(await promise);
      }
      console.log(results);
    }
    内容涉及的不是很深入,后续会继续补充和修改,希望大家多多指正
  • 相关阅读:
    abc
    与7无关的数
    字符串排序
    质因数的个数
    符号运算
    底层代码创建GUI
    图像处理基础---RGB图 灰度图 索引图 调色板
    82.游戏项目-椭圆轨迹的实现
    81.游戏项目-物体任意角度飞行和停止
    80.游戏项目-物体的移动
  • 原文地址:https://www.cnblogs.com/zhuzhenzhen/p/5053271.html
Copyright © 2020-2023  润新知