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

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

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

推荐文章

使用 sync.Pool 优化 Go 程序性能
2024-11-19 05:56:51 +0800 CST
Vue 中如何处理父子组件通信?
2024-11-17 04:35:13 +0800 CST
Node.js中接入微信支付
2024-11-19 06:28:31 +0800 CST
全新 Nginx 在线管理平台
2024-11-19 04:18:33 +0800 CST
html流光登陆页面
2024-11-18 15:36:18 +0800 CST
nginx反向代理
2024-11-18 20:44:14 +0800 CST
防止 macOS 生成 .DS_Store 文件
2024-11-19 07:39:27 +0800 CST
介绍Vue3的Tree Shaking是什么?
2024-11-18 20:37:41 +0800 CST
使用Vue 3实现无刷新数据加载
2024-11-18 17:48:20 +0800 CST
MySQL 日志详解
2024-11-19 02:17:30 +0800 CST
JavaScript设计模式:单例模式
2024-11-18 10:57:41 +0800 CST
Nginx 防止IP伪造,绕过IP限制
2025-01-15 09:44:42 +0800 CST
JavaScript数组 splice
2024-11-18 20:46:19 +0800 CST
Vue中的`key`属性有什么作用?
2024-11-17 11:49:45 +0800 CST
Vue 3 中的 Fragments 是什么?
2024-11-17 17:05:46 +0800 CST
php腾讯云发送短信
2024-11-18 13:50:11 +0800 CST
程序员茄子在线接单