# 异步编程

# 同步和异步的概念

# 同步

执行某个任务时,没有得到结果前,不会执行后续的操作。

# 异步

一个异步任务执行后,没有得到结果之前,就可以继续执行后续操作。异步任务完成后,一般通过回调通知调用者。例如setTimeout,fetch/XMLHttpRequest等。

# 使用回调函数

缺点

  • 回调地狱
  • 高度耦合
  • 不易维护
  • 不能直接return

# 事件驱动

优点

  • 去耦合
  • 便于实现模块化

缺点

  • 运行流程不清晰
  • 阅读代码困难

代码示例

// login.js
let loginEvent = new CustomEvent("login-over", {
    token: "token"
});

function login() {
    setTimeout(() => {
        window.dispatchEvent(loginEvent);
    }, 3000);
}
// getOrderId.js
function getOrderId(token) {
    if(token){
        setTimeout(() => {
            window.dispatchEvent(orderIdEvent);
        }, 2000);
    }
}

function tokenListener(ev) {
    getOrderId(ev.token);
}
//在window 上添加监听事件
window.addEventListener("login-over", tokenListener);

# 发布订阅

也是使用事件驱动,但是有一个事件中心,可以查看消息流转。

// MsgCenter.js
/*
 * 消息中心
 * @class MsgCenter
 */
class MsgCenter {
  constructor() {
    this.listeners = {};
  }
  /**
   *
   *
   * 订阅
   * @memberOf MsgCenter
   */
  subscribe(type, listener) {
    if (this.listeners[type] === undefined) {
      this.listeners[type] = [];
    }
    this.listeners[type].push(listener);
    console.log(`${type}消息订阅数:${this.listeners[type].length}`);
    return listener;
  }

  /**
   *
   *
   * 发送
   * @memberOf MsgCenter
   */
  dispatch(type, args = {}) {
    if (!type) {
      throw new Error("Event object missing 'type' property.");
    }
    if (this.listeners[type] instanceof Array) {
      const listeners = this.listeners[type];
      for (let j = 0; j < listeners.length; j++) {
        listeners[j].call(this, type, args);
      }
    }
  }

  /**
   *
   *
   * 取消订阅
   * @memberOf MsgCenter
   */
  unSubscribe(type, listener) {
    if (this.listeners[type] instanceof Array) {
      const listeners = this.listeners[type];
      for (let i = 0; i < listeners.length; i++) {
        if (listeners[i] === listener) {
          listeners.splice(i, 1);
          break;
        }
      }
      console.log(`${type}消息订阅数:${this.listeners[type].length}`);
    }
  }

  /**
   *
   * 获取某种消息所有订阅
   * @param {any} type
   * @returns
   *
   * @memberOf MsgCenter
   */
  getTypeSubscribe(type) {
    return this.listeners[type] || [];
  }

  /**
   *
   *
   * 销毁
   * @memberOf MsgCenter
   */
  destroy() {
    this.listeners = null;
  }
}
const MyMsgCenter = new MsgCenter();

export default MyMsgCenter;

// login.js
import MyMsgCenter from "./MsgCenter.js";
export function login() {
    setTimeout(() => {
        MyMsgCenter.dispatch("login-over",{ token:"token" });
    }, 3000);
}
// getOrderId.js
import MyMsgCenter from "./MsgCenter.js";
function getOrderId(token) {
    if(token){
       console.log(token)
    }
}

function tokenListener(type,ev) {
    getOrderId(ev.token);
    MyMsgCenter.unSubscribe(type, tokenListener);
}

MyMsgCenter.subscribe("login-over", tokenListener); 

# Promise

优点

链式调用,流程清晰

缺点

  • 代码冗余,不够简洁
  • 无法取消Promise
  • 错误需要回调函数捕获

# Generator

  • 优点:可以控制函数执行
  • 缺点:执行时机太麻烦
function login() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("token");
    }, 3000);
  });
}

function getOrderId(token) {
  return new Promise((resolve, reject) => {
    if (token) {
      setTimeout(() => {
        resolve("orderId");
      }, 2000);
    }
  });
}

function orderDetails(orderId) {
  return new Promise((resolve, reject) => {
    if (orderId) {
      setTimeout(() => {
        resolve("淘宝订单:购买xxx书一本");
      }, 1500);
    }
  });
}

function* execute() {
  const token = yield login();
  const orderId = yield getOrderId(token);
  const orderInfo = yield orderDetails(orderId);
}

let g = execute();

let { value, done } = g.next();

value.then((token) => {
  console.log("token==", token);
  let { value, done } = g.next(token);
  value.then((orderId) => {
    console.log("orderId==", orderId);
    let { value, done } = g.next(orderId);
    value.then((orderInfo) => {
      console.log("orderInfo==", orderInfo);
    });
  });
});

# Async&Await

TIP

  • Async函数 是Generator函数的语法糖
  • 语义更好,内置执行器
  • 缺点:蝴蝶效应 一处async 一处await
function login() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("token");
    }, 3000);
  });
}

function getOrderId(token) {
  return new Promise((resolve, reject) => {
    if (token) {
      setTimeout(() => {
        resolve("orderId");
      }, 2000);
    }
  });
}

function orderDetails(orderId) {
  return new Promise((resolve, reject) => {
    if (orderId) {
      setTimeout(() => {
        resolve("淘宝订单:购买xxx书一本");
      }, 1500);
    }
  });
}

async function execute() {
  const token = await login();
  const orderId = await getOrderId(token);
  const orderInfo = await orderDetails(orderId);
  console.log(orderInfo);
}

execute();
Last Updated: 10/30/2022, 9:56:09 PM