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

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

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 编程 异步编程

推荐文章

15 个你应该了解的有用 CSS 属性
2024-11-18 15:24:50 +0800 CST
Vue3中如何处理状态管理?
2024-11-17 07:13:45 +0800 CST
2025年,小程序开发到底多少钱?
2025-01-20 10:59:05 +0800 CST
Vue3中的组件通信方式有哪些?
2024-11-17 04:17:57 +0800 CST
平面设计常用尺寸
2024-11-19 02:20:22 +0800 CST
Golang 几种使用 Channel 的错误姿势
2024-11-19 01:42:18 +0800 CST
Vue3 结合 Driver.js 实现新手指引
2024-11-18 19:30:14 +0800 CST
PHP 如何输出带微秒的时间
2024-11-18 01:58:41 +0800 CST
Go 中的单例模式
2024-11-17 21:23:29 +0800 CST
Mysql允许外网访问详细流程
2024-11-17 05:03:26 +0800 CST
赚点点任务系统
2024-11-19 02:17:29 +0800 CST
Vue3中如何处理异步操作?
2024-11-19 04:06:07 +0800 CST
JavaScript设计模式:单例模式
2024-11-18 10:57:41 +0800 CST
地图标注管理系统
2024-11-19 09:14:52 +0800 CST
Vue 中如何处理跨组件通信?
2024-11-17 15:59:54 +0800 CST
一个简单的html卡片元素代码
2024-11-18 18:14:27 +0800 CST
Web浏览器的定时器问题思考
2024-11-18 22:19:55 +0800 CST
Go语言SQL操作实战
2024-11-18 19:30:51 +0800 CST
聚合支付管理系统
2025-07-23 13:33:30 +0800 CST
api远程把word文件转换为pdf
2024-11-19 03:48:33 +0800 CST
使用Python实现邮件自动化
2024-11-18 20:18:14 +0800 CST
thinkphp swoole websocket 结合的demo
2024-11-18 10:18:17 +0800 CST
程序员茄子在线接单