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

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

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

推荐文章

前端项目中图片的使用规范
2024-11-19 09:30:04 +0800 CST
curl错误代码表
2024-11-17 09:34:46 +0800 CST
前端开发中常用的设计模式
2024-11-19 07:38:07 +0800 CST
Go 开发中的热加载指南
2024-11-18 23:01:27 +0800 CST
Golang 中你应该知道的 noCopy 策略
2024-11-19 05:40:53 +0800 CST
MySQL 日志详解
2024-11-19 02:17:30 +0800 CST
css模拟了MacBook的外观
2024-11-18 14:07:40 +0800 CST
PHP 压缩包脚本功能说明
2024-11-19 03:35:29 +0800 CST
回到上次阅读位置技术实践
2025-04-19 09:47:31 +0800 CST
Vue3中哪些API被废弃了?
2024-11-17 04:17:22 +0800 CST
手机导航效果
2024-11-19 07:53:16 +0800 CST
实用MySQL函数
2024-11-19 03:00:12 +0800 CST
Go 单元测试
2024-11-18 19:21:56 +0800 CST
Vue3中如何处理组件间的动画?
2024-11-17 04:54:49 +0800 CST
CSS 中的 `scrollbar-width` 属性
2024-11-19 01:32:55 +0800 CST
20个超实用的CSS动画库
2024-11-18 07:23:12 +0800 CST
防止 macOS 生成 .DS_Store 文件
2024-11-19 07:39:27 +0800 CST
程序员茄子在线接单