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

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

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

推荐文章

Golang - 使用 GoFakeIt 生成 Mock 数据
2024-11-18 15:51:22 +0800 CST
php strpos查找字符串性能对比
2024-11-19 08:15:16 +0800 CST
Nginx 反向代理 Redis 服务
2024-11-19 09:41:21 +0800 CST
Golang 随机公平库 satmihir/fair
2024-11-19 03:28:37 +0800 CST
Go中使用依赖注入的实用技巧
2024-11-19 00:24:20 +0800 CST
php指定版本安装php扩展
2024-11-19 04:10:55 +0800 CST
windon安装beego框架记录
2024-11-19 09:55:33 +0800 CST
地图标注管理系统
2024-11-19 09:14:52 +0800 CST
HTML和CSS创建的弹性菜单
2024-11-19 10:09:04 +0800 CST
使用 sync.Pool 优化 Go 程序性能
2024-11-19 05:56:51 +0800 CST
html一个包含iPhoneX和MacBook模拟器
2024-11-19 08:03:47 +0800 CST
纯CSS绘制iPhoneX的外观
2024-11-19 06:39:43 +0800 CST
页面不存在404
2024-11-19 02:13:01 +0800 CST
任务管理工具的HTML
2025-01-20 22:36:11 +0800 CST
SQL常用优化的技巧
2024-11-18 15:56:06 +0800 CST
php 统一接受回调的方案
2024-11-19 03:21:07 +0800 CST
程序员茄子在线接单