美文网首页
Javascript 异步编程方法汇总

Javascript 异步编程方法汇总

作者: 五秋木 | 来源:发表于2019-10-24 16:02 被阅读0次
    1. 回调函数
      假定有两个函数f1和f2,后者等待前者的执行结果。
      f1(); f2();
      如果f1是一个很耗时的任务,可以考虑改写为f1,把f2写为f1的回调函数。
      function f1(callback){
         setTimeout(function(){
              //f1代码
              callback();
          },1000);
      }
      
      执行的时候就变成了f1(f2); 采用这种方法把同步变成异步,f1不会堵塞程序运行,相当于先执行程序的主要逻辑,将耗时都部分推迟执行。
      回调函数优点:简单、容易理解和部署;缺点:不利于代码的阅读和维护。
      一个同步(阻塞)中使用回调的例子,目的是在func1代码执行完成后执行func2代码。
      var func1 = function(callback){
             // do something 
            (callback && typeof(callback) === "function") && callback();
      }
      func1(func2);
      var func2 = function(){}
      

    更多回调函数请参考:

    1. 事件监听
      任务的执行不取决于代码的顺序,而取决于某件事件是否发生。
      为f1绑定一个事件:f1.on('done', f2); 当f1发生done事件,就执行f2。然后对f1进行改写为
      function f1(){
      setTimeout(function*(){
           // f1的任务代码
           f1.trigger('done');
          },1000);
      }
      
      优点:比较容易理解,绑定多个事件,每个事件可以指定多个回调函数,去耦合,实现模块化。
    2. 发布/订阅
      例如:jquery中一个插件:Tiny Pub/Sub
      1. f2向信号中心"jquery订阅"done信号: jquery.subscribe("done", f2);
      2. f1函数如下:
        function f1(){
            setTimeout(function(){
                  // f1的执行代码
                   jquey.publish('done');
            }, 1000);
         }
        
        jquey.publish('done');就是f1执行完毕之后,向信号中心发送done信号,从而引发f2的执行。f2执行后,也可以取消订阅jquery.unsubscribe("done", f2);
    3. Promises对象:为异步编程提供统一接口。
      思想:每一个异步任务返回一个Promise对象,该对象有一个then方法,允许指向回调函数。
      Promise的三种状态:
      • Pending:Promise对象实例创建时候的初始状态。
      • Fulfilled:成功的状态
      • Rejected:失败的状态
        Promise ---> resolved ---> then(回调callback) 或者 ---> rejected ---> catch(回调callback)。Promise一旦从等待状态变成其他状态就永远不能改变状态了。
         const instance = new Promise((resolve, reject) => {
           // 一些异步操作
           if(/*异步操作成功*/) {
               resolve(value);
           } else {
               reject(error);
               }
           }
         })
         instance.then(value => {
             // do something...
          }, error => {
               // do something...
          })
        
        构造函数内部的代码立刻执行。
        then 方法会返回一个新的 Promise 实例,可以分两种情况来看:
        • 指定返回值是新的 Promise 对象,如return new Promise(...),这种情况没啥好说的,由于返回的是 Promise,后面显然可以继续调用then方法。
        • 返回值不是Promise, 如:return 1 这种情况还是会返回一个 Promise,并且这个Promise 立即执行回调 resolve(1)。所以仍然可以链式调用then方法。(注:如果没有指定return语句,相当于返回了undefined)。
        1. 示例一
        function sayHi(name) {
           return new Promise((resolve, reject) => {
                 setTimeout(() => {
                     resolve(name);
                  }, 2000)
            })
        }
        sayHi('张三')
           .then(name => {
                 console.log(`你好, ${name}`);
                 return sayHi('李四');    // 最终 resolved 函数中的参数将作为值传递给下一个then
              })
             // name 是上一个then传递出来的参数
           .then(name => {                
               console.log(`你好, ${name}`);
               return sayHi('王二麻子');
             })
           .then(name => {
               console.log(`你好, ${name}`);
           })
         // 你好, 张三
         // 你好, 李四
         // 你好, 王二麻子
        
        1. 示例二
         read('./file-01.txt', 'utf8')
            .then(data => {
              // 因为 read 方法会返回一个 promise ,返回read执行相当于返回一个 promise
              // 会将这个 promise 的执行成功的结果传递给下一次 then 的 resolve 
                  return read(data, 'utf8');
              }, err => {
                  console.log(err);
            })
            .then(data => {
              // 如果返回一个普通的值 ,会将这个普通值传递倒下一次 then 的成功的参数
                return [data];
             }, err => {
               console.log(err);
             })
            .then(data => {
              // 返回的是一个普通值
               console.log(data);
               // 没有写 return ,相当于返回一个 undefined ,下一个then的成功值则为 undefined
                }, err => {
                console.log(err);
              })
            .then(data => {
            // 上一个 then 没有返回值,默认值为 undefined
            // undefined 也算成功
              console.log(data);
              // 抛出错误,将传给下一个 then 的 reject 
                throw new Error('xxx');
            }, err => {
                console.log(err);
          })
           .then(null, err => {
              // 如果上一个 then 抛出错误,最近的 reject 会执行
              // reject 执行后默认下一个 then 会接收 undefined 
            console.log(err);
          })
          .then(data => {
              // 上一个 then 中失败没有返回值,默认为 undefined 
              console.log('resolve');        
            }, err => {
              console.log('reject');
          });
        
        1. 示例三:使用catch代替err
         read('./file-01.txt', 'utf8')
            .then(data => {
                return read(data, 'utf8');
            })
          .then(data => {
              return [data];
          })
          .then(data => {
              console.log(data);
          })
          .then(data => {
            console.log(data);
              // 抛出错误后,找到最近的接收错误方法
              // 如果所有的 then 都没有 reject 方法,则找最后一个 catch
              throw new Error('xxx');
            })
          .then(null)
          .then(data => {
              console.log('resolve');        
            })
          .catch(err => {
              console.log(err);
          });
        
    4. async/await
      • async/await是基于Promise实现的,不能用于普通的回调函数。
      • 和Promise一样,是非阻塞的。
      • 使异步代码看起来像是同步代码。
      • 一个函数如果加上async,那么该函数就会返回一个Promise,(如果指定的返回值不是Promise对象,也返回一个Promise,只不过立即 resolve ,处理方式同 then 方法,因此 async 函数通过 return 返回的值,会成为 then 方法中回调函数的参数。单独一个 async 函数,其实与Promise执行的功能是一样的。
      • await 就是异步等待,它等待的是一个Promise,因此 await 后面应该写一个Promise对象,如果不是Promise对象,那么会被转成一个立即 resolve 的Promise
      1. 实例一
          let fs = require('fs')
          function read(file) {
          return new Promise(function(resolve, reject) {
              fs.readFile(file, 'utf8', function(err, data) {
              if (err) reject(err)
              resolve(data)
              })
          })
          }
          async function readResult(params) {
          try {
              let p1 = await read(params, 'utf8')
              //await后面跟的是一个Promise实例,
              //await返回的结果直接return的结果,不需要进行新的then操作。
              let p2 = await read(p1, 'utf8')
              let p3 = await read(p2, 'utf8')
              console.log('p1', p1)
              console.log('p2', p2)
              console.log('p3', p3)
              return p3
          } catch (error) {
              console.log(error)
          }
          }
          readResult('1.txt').then( // async函数返回的也是个promise
          data => {
              console.log(data)
          },
          err => console.log(err)
          )
          // p1 2.txt
          // p2 3.txt
          // p3 结束
          // 结束
        
      2. 示例二
      function readAll() {
         read1()
         read2()//这个函数同步执行
        }
      async function read1() {
         let r = await read('1.txt','utf8')
         console.log(r)
       }
      async function read2() {
         let r = await read('2.txt','utf8')
         console.log(r)
       }
      readAll() // 2.txt 3.txt
      
      1. 示例三
      async function func() {
          try {
              const num1 = await 200;
              console.log(`num1 is ${num1}`);
              const num2 = await Promise.reject('num2 is wrong!');
              console.log(`num2 is ${num2}`);
              const num3 = await num2 + 100;
              console.log(`num3 is ${num3}`);
          } catch (error) {
              console.log(error);
          }
       }
      
      func();
      // num1 is 200
      // 出错了
      // num2 is wrong!
      

    参考:
    - Javascript异步编程的4种方法
    - 异步方法的发展流程
    - JS 异步编程六种方案

    相关文章

      网友评论

          本文标题:Javascript 异步编程方法汇总

          本文链接:https://www.haomeiwen.com/subject/uhvqvctx.html