编程 JavaScript中如何从外部解决Promise,特别是在需要等待一个异步操作完成后再执行另一个操作的场景

2024-11-19 02:17:31 +0800 CST views 773

JavaScript中如何从外部解决Promise,特别是在需要等待一个异步操作完成后再执行另一个操作的场景

这是JavaScript中那些在现实世界中极其强大的技巧之一,适用于处理异步操作的各种场景,尤其是需要在外部控制Promise的解决或拒绝时。以下是一个典型的应用场景和解决方案。

强大的实际应用场景:动作(A)等待另一个动作(B)

假设我们有两个异步操作,用户在执行动作(A)时需要等待动作(B)完成。举个例子,一个社交应用中,用户可以创建、保存和发布帖子(类似于Medium)。如果用户希望在帖子保存后发布,该如何处理?

HTML结构

<p>
  Save status:
  <b><span id="save-status">Not saved</span></b>
</p>
<p>
  Publish status:
  <b><span id="publish-status">Not published</span></b>
</p>
<button id="save">Save</button>
<button id="publish">Publish</button>

问题:用户想在帖子保存时发布

如果用户点击发布按钮时,帖子还没有保存,应该等待帖子保存完毕后再发布。这需要确保在发布之前,帖子已经保存。

JavaScript 代码

saveButton.onclick = () => {
  save();
};

publishButton.onclick = async () => {
  await publish();
};

let saveResolve;
let hasSaved = false;

async function save() {
  hasSaved = false;
  saveStatus.textContent = 'Saving...';
  // ✅ 从外部解决Promise
  await makeSaveRequest();
  saveResolve();
  hasSaved = true;
  saveStatus.textContent = 'Saved';
}

async function waitForSave() {
  if (!hasSaved) {
    await new Promise((resolve) => {
      saveResolve = resolve;
    });
  }
}

async function publish() {
  publishStatus.textContent = 'Waiting for save...';
  await waitForSave();
  publishStatus.textContent = 'Published';
}

解释:

  1. saveResolve 是一个用于在 save() 完成后手动解决的 Promise
  2. waitForSave() 函数在保存未完成时,使用新的 Promise 等待保存完成。
  3. 一旦 save() 完成,外部 saveResolve() 将被调用,Promise 解决,发布操作继续。

使用 Deferred 类简化代码

为了解决外部Promise的逻辑,我们可以抽象出一个 Deferred 类,它可以更好地管理Promise的解决和拒绝。

class Deferred {
  constructor() {
    this.promise = new Promise((resolve, reject) => {
      this.resolve = resolve;
      this.reject = reject;
    });
  }
}

const deferred = new Deferred();
// 在外部解决
deferred.resolve();

重构后的代码

const deferredSave = new Deferred();
let hasSaved = false;

async function save() {
  hasSaved = false;
  saveStatus.textContent = 'Saving...';
  // ? 从外部解决Promise
  await makeSaveRequest();
  deferredSave.resolve();
  hasSaved = true;
  saveStatus.textContent = 'Saved';
}

async function waitForSave() {
  if (!hasSaved) await deferredSave.promise;
}

async function publish() {
  publishStatus.textContent = 'Waiting for save...';
  await waitForSave();
  publishStatus.textContent = 'Published';
}

使用 Deferred 类使代码更简洁,同时保留了相同的功能。Deferred 类可以很好地管理Promise,避免了手动保存 resolvereject 的麻烦。

将事件流转换为Promise

在某些场景下,可能需要将事件流转换为Promise。这时可以利用 Deferred,实现如下:

// data-fetcher.js
const deferred = new Deferred();

let dataDeferred;
function startListening() {
  dataDeferred = new Deferred();
  eventStream.on('data', (data) => {
    dataDeferred.resolve(data);
  });
}

async function getData() {
  return await dataDeferred.promise;
}

// client.js
const { startListening, getData } = require('./data-fetcher.js');
startListening();
// ? 等待事件发生后返回
const data = await getData();

解释:

  1. data-fetcher.js 中,我们通过 startListening() 开始监听事件流。
  2. 当事件流触发 data 时,调用 dataDeferred.resolve(data) 解决Promise。
  3. 客户端代码通过 await getData() 等待事件数据的返回。

最后的思考

从外部解决Promise是一种非常强大的技术,能够让你的代码在处理异步事件和用户操作时更加灵活和清晰。通过使用 Deferred 类或类似的库(如 ts-deferred),可以更轻松地管理异步流程。

复制全文 生成海报 JavaScript 编程 异步编程

推荐文章

WebSocket在消息推送中的应用代码
2024-11-18 21:46:05 +0800 CST
如何使用go-redis库与Redis数据库
2024-11-17 04:52:02 +0800 CST
Rust 并发执行异步操作
2024-11-19 08:16:42 +0800 CST
mysql关于在使用中的解决方法
2024-11-18 10:18:16 +0800 CST
jQuery `$.extend()` 用法总结
2024-11-19 02:12:45 +0800 CST
css模拟了MacBook的外观
2024-11-18 14:07:40 +0800 CST
Web浏览器的定时器问题思考
2024-11-18 22:19:55 +0800 CST
Go 并发利器 WaitGroup
2024-11-19 02:51:18 +0800 CST
前端项目中图片的使用规范
2024-11-19 09:30:04 +0800 CST
Vue3中如何处理SEO优化?
2024-11-17 08:01:47 +0800 CST
如何在Vue中处理动态路由?
2024-11-19 06:09:50 +0800 CST
前端代码规范 - 图片相关
2024-11-19 08:34:48 +0800 CST
Vue 中如何处理跨组件通信?
2024-11-17 15:59:54 +0800 CST
15 个你应该了解的有用 CSS 属性
2024-11-18 15:24:50 +0800 CST
# 解决 MySQL 经常断开重连的问题
2024-11-19 04:50:20 +0800 CST
底部导航栏
2024-11-19 01:12:32 +0800 CST
PHP中获取某个月份的天数
2024-11-18 11:28:47 +0800 CST
程序员茄子在线接单