从回调函数到Promise及实战案例,一次搞懂异步编程

函数的执行分为同步和异步两种。
同步即为 同步连续执行,通俗点讲就是做完一件事,再去做另一件事。
异步即为 先做一件事,中间可以去做其他事情,稍后再回来做第一件事情。

基于Callback 与 Promise 以及 Async/await实现下面的案例

三个红绿黄三个颜色的灯,然后每个灯之间会进行切换,比如绿灯1s亮然后黄灯亮1s,最后是红灯1s,一个流程过后则是进行一个循环,重新到绿灯亮。

Callback

所有异步编程方案的根基
有调用者定义,交给执行者的函数。将函数作为参数传递

// 实现亮灯的基本方法方法
const lamp = (color, callback) => {
  // 打印灯的颜色
  console.log(`灯颜色: ${color}`);
  // 使用定时器,延时一秒
  setTimeout(() => {
    // 触发回调函数
    callback();
  }, 1000);
};

函数依次交替执行

lamp("red", () => {
  lamp("yellow", () => {
    lamp("green", () => {
      console.log("执行完成");
    });
  });
});

// 控制台打印
// 灯颜色: red
// 灯颜色: yellow
// 灯颜色: green
// 执行完成

通过递归使函数循环依次交替执行

const run = () =>{
  lamp("red", () => {
    lamp("yellow", () => {
      lamp("green", () => {
        run();
      });
    });
  });
}
run()
// 控制台打印
// 灯颜色: red
// 灯颜色: yellow
// 灯颜色: green
// 执行完成

Promise

CommonJs 为异步编程提供统一、更合理、更强大的解决方案

// 实现亮灯的基本方法方法
const lamp = (color) => {
  // 返回一个promise对象
  return new Promise((resolve, reject) => {
    // 打印灯的颜色
    console.log(`灯颜色: ${color}`);
    // 使用定时器,延时一秒
    setTimeout(() => {
      // 触发成功承诺
      resolve();
    }, 1000);
  });
};

函数依次交替执行

// 类似callback方式实现(未解决了回调地狱的问题)
lamp("red").then(() => {
  lamp("yellow").then(() => {
    lamp("green").then(() => {
      console.log("执行完成");
    });
  });
});
// 控制台打印
// 灯颜色: red
// 灯颜色: yellow
// 灯颜色: green
// 执行完成

// 链式调用方式实现 (解决了回调地狱的问题,语义相对清晰)
// Promise 对象的then方法会返回一个全新的Promise对象
// 后面的then方法就是在为上一个then返回的Promise注册回调
// 前面的then方法中回调函数的返回值会作为后面then方法的回调参数
// 如果回调中返回的是Promise,那后面的then方法的回调就会等待它的结束
lamp("red")
  .then(() => {
    return lamp("yellow");
  })
  .then(() => {
    return lamp("green");
  })
  .then(() => {
    console.log("执行完成");
  });
// 控制台打印
// 灯颜色: red
// 灯颜色: yellow
// 灯颜色: green
// 执行完成

通过递归使函数循环依次交替执行

const run = () => {
  lamp("red")
    .then(() => {
      return lamp("yellow");
    })
    .then(() => {
      return lamp("green");
    })
    .then(() => {
      run();
    });
};
run()
// 控制台打印
// 灯颜色: red
// 灯颜色: yellow
// 灯颜色: green
// 执行完成

Generator

// 实现亮灯的基本方法方法
const lamp = (color) => {
  // 返回一个promise对象
  return new Promise((resolve, reject) => {
    // 打印灯的颜色
    console.log(`灯颜色: ${color}`);
    // 使用定时器,延时一秒
    setTimeout(() => {
      // 触发成功承诺
      resolve();
    }, 1000);
  });
};

函数依次交替执行

function* run() {
  yield lamp("red");
  yield lamp("green");
  yield lamp("yellow");
}

const co = run();
co.next()
  .value.then(() => {
    return co.next().value;
  })
  .then(() => {
    return co.next().value;
  })
  .then(() => {
    return co.next().value;
  });

通过递归使函数循环依次交替执行

function* run() {
  yield lamp("red");
  yield lamp("green");
  yield lamp("yellow");
}

const run2 = () => {
  const co = run();
  co.next()
    .value.then(() => {
      return co.next().value;
    })
    .then(() => {
      return co.next().value;
    })
    .then(() => {
      return co.next().value;
    })
    .then(() => {
      run2();
    });
};

run2();

Async/Await语法糖 ECMA2017

// 实现亮灯的基本方法方法
const lamp = (color) => {
  // 返回一个promise对象
  return new Promise((resolve, reject) => {
    // 打印灯的颜色
    console.log(`灯颜色: ${color}`);
    // 使用定时器,延时一秒
    setTimeout(() => {
      // 触发成功承诺
      resolve();
    }, 1000);
  });
};

// 执行方法
const run = async () => {
  await lamp("red");
  await lamp("green");
  await lamp("yellow");
  run();
};

// 执行
run()

callback转换成promise

需求在写小程序的时候有很多wx.xxx 方法不支持 promise 模式调用,但是支持 success 与 fail 的callback回调方式。所以如何有效的封装一个函数,优雅的调用 wx.xxx

const promiseFn = function (func, params = {}) {
  // 返回promise对象
  return new Promise((resolve, reject) => {
    // 传入参数混入 success 与 fail
    const args = Object.assign(params, {
      success: (res) => {
        // 成功承诺
        resolve(res);
      },
      fail: (error) => {
        // 失败承诺
        reject(error);
      },
    });
    // 调用传入的函数
    func(args);
  });
};

// 模拟测试函数
const wx = (options) => {
  setTimeout(() => {
    options.success && options.success(options.key);
  }, 100);
};

// 通过promiseFn 调用 wx 函数,并且传递对象为参数
promiseFn(wx, { key: 'hello' })
  .then((res) => {
    console.log(res);
  });
// 控制台打印
// hello

并行请求如何处理

进入一个页面或者一个组件,经常会向后端发送2个或更多的请求。如何使用promise优雅的处理呢

// 模拟ajax请求发送
const ajax = (data) =>
  new Promise((resolve, _reject) => {
    setTimeout(() => {
      console.log(`ajax: ${data.key}`);
      resolve(data);
      // 因为使用了随机数延时,所以请求先后可能不一致
    }, Math.round(Math.random() * 1000));
  });

const loadData = async () => {
  // showLoading()
  // 如果需要等待三个请求完毕
  const [ res1,res2,res3 ] = await Promise.all([
    ajax({ key: "请求1" }),
    ajax({ key: "请求2" }),
    ajax({ key: "请求3" }),
  ]);
  console.log(res1,res2,res3)
  // hiddenLoading()
};

loadData();

并行请求加入了串行如何处理

请求1``请求2``请求3 同时提交,请求4 依赖于请求2的结果

// 模拟ajax请求发送
const ajax = (data) =>
  new Promise((resolve, _reject) => {
    setTimeout(() => {
      console.log(`ajax: ${data.key}`);
      resolve(data);
    }, Math.round(Math.random() * 1000));
  });

// 组合请求2 结束后 执行请求4
const ajax2And4 = async () => {
  const res = await ajax({ key: "请求2" });
  const res2 = await ajax({ key: res.key + "_请求4" });
  return res2;
};
// 加载方法
const loadData = async () => {
  // showLoading()
  const [res1, res2, res3] = await Promise.all([
    ajax({ key: "请求1" }),
    // 调用组合封装的函数
    ajax2And4(),
    ajax({ key: "请求3" }),
  ]);
  console.log(res1, res2, res3);
  // hiddenLoading()
};
loadData();

链式调用实现方式,当程序较为复杂时 此种写法逻辑不是很清晰且自由度不高

实现ajax请求超时控制方式

只是将实现思路,因为目前的请求类库都已支持超时设置,所以不要在生产代码中使用

// 模拟ajax请求发送
const ajax = (data) =>
  new Promise((resolve, _reject) => {
    setTimeout(() => {
      console.log(`ajax: ${data.key}`);
      resolve(data);
    }, Math.round(Math.random() * 1000));
  });

// 延时函数
const timerOutFn = (timeout) =>
  new Promise((_resolve, reject) => {
    setTimeout(() => reject(new Error("timeout")), timeout);
  });

// 使用新函数包裹 ajax 与延时函数
const ajaxAndTimer = async (params, { timeout = 500 }) => {
  return await Promise.race([ajax(params), timerOutFn(timeout)]);
};

// 使用
ajaxAndTimer({ key: "请求1" }, { timeout: 200 });

// 控制台打印
// 未超时
// ajax: 请求1
// 超时
// UnhandledPromiseRejectionWarning: Error: timeout
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

赵忠洋

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值