这篇文章通过一个小案例,一起了解Promise的链式调用
案例分析
这里我模拟某个语音功能的操作流程,每隔一秒钟,我去模拟一个操作,先看下面代码:
const chain = Promise.resolve();
chain
.then(() => {
// 1.模拟开始说话 打开vc vc状态speaking
vc.value?.changeState("SPEAKING");
})
.then(() => {
// 2. 语音输入
setTimeout(() => {
question.value = "你好";
}, 1000);
})
.then(() => {
// 2. 继续输入
setTimeout(() => {
question.value = "你好,欢迎使用vc";
}, 1000);
});
问题解读
有没有发现上面代码有什么不妥的地方吗?
这段代码是一个使用 Promise 链式调用的示例,主要用于模拟某个语音控制(VC)功能的操作流程。
- 初始化 Promise 链
const chain = Promise.resolve();
这里,Promise.resolve()
创建了一个立即解析(fulfilled)的 Promise,它返回一个 Promise 对象,这个对象的状态已经被设置为解析(fulfilled),所以任何连接到这个 Promise 的 .then()
方法都会立即执行。
2. 第一个 .then()
.then(() => {
// 1.模拟开始说话 打开vc vc状态speaking
vc.value?.changeState("SPEAKING");
})
当 Promise 解析后,第一个 .then()
会被执行。这里,它模拟了开始说话的动作,并尝试更改 vc
(可能是一个代表语音控制的对象)的状态为 “SPEAKING”。使用了可选链(?.
),意味着如果 vc
或 vc.value
为 null
或 undefined
,则不会调用 changeState
方法,也不会报错。
3. 第二个 .then()
.then(() => {
// 2. 语音输入
setTimeout(() => {
question.value = "你好";
}, 1000);
})
第二个 .then()
模拟了语音输入的过程。它使用 setTimeout
来模拟一个延迟(这里是1秒),之后更改 question.value
的值为 “你好”。这可以理解为在1秒后,模拟用户通过语音输入了“你好”。
但这里有一个问题:由于 setTimeout
是异步的,而 .then()
的回调函数是立即返回的,所以下一个 .then()
可能会在 setTimeout
的回调执行之前就被调用。
4. 第三个 .then()
.then(() => {
// 3. 继续输入
setTimeout(() => {
question.value = "你好,欢迎使用vc";
}, 1000);
})
第三个 .then()
的逻辑与第二个相似,但它在 question.value
中添加了更多的文本。但这里又有一个同样的问题:它可能在上一个 setTimeout
执行完之前就运行了。
修改建议
- 连续的
.then()
调用之间并没有真正的依赖关系,它们只是简单地串联在一起。但由于setTimeout
的使用,它们之间的执行顺序可能并不是你预期的。 - 如果你希望每一步都在前一步的
setTimeout
之后执行,你应该将每一步都放在一个返回 Promise 的函数中,并在该 Promise 解析后再进行下一步。
修正后的代码可能类似于这样(使用了 async/await
和自定义的延迟函数):
async function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function simulateVC() {
// 1.模拟开始说话 打开vc vc状态speaking
vc.value?.changeState("SPEAKING");
await delay(1000);
// 2. 语音输入
question.value = "你好";
await delay(1000);
// 3. 继续输入
question.value = "你好,欢迎使用vc";
}
simulateVC();
这样,每一步都会在前一步之后准确地执行。
还有什么其他方式实现这种延迟调用链的吗?
除了使用 async/await
和 Promise 之外,还有其他几种方式可以实现延迟调用链。以下是其中的一些方法:
1. 回调函数(Callback)
使用回调函数是最基本的方法之一,但它可能会导致所谓的“回调地狱”(Callback Hell),特别是当你有多个依赖的异步操作时。
function delayedLog(msg, delay, callback) {
setTimeout(() => {
console.log(msg);
callback && callback();
}, delay);
}
delayedLog('Hello', 1000, () => {
delayedLog('World', 1000);
});
2. Promise 链
你已经展示了 Promise 链的方式,但是为了更清晰地表达每一步的延迟,我们可以稍微调整一下代码结构。
function delayedPromise(msg, delay) {
return new Promise(resolve => {
setTimeout(() => {
console.log(msg);
resolve();
}, delay);
});
}
delayedPromise('Hello', 1000)
.then(() => delayedPromise('World', 1000));
3. Promise.all 和数组映射
如果你有一个需要依次延迟执行的任务数组,你可以使用 Promise.all
结合数组的 map
方法。不过要注意,Promise.all
会等待所有 Promise 都解决,但如果你想要依次执行它们,你应该使用 reduce
。
const tasks = ['Hello', 'World'];
const delays = [1000, 1000];
tasks.reduce((chain, task, index) => {
return chain
.then(() => new Promise(resolve => {
setTimeout(() => {
console.log(task);
resolve();
}, delays[index]);
}));
}, Promise.resolve());
4. Generator 函数和 co 库
在 ES6 之前,Generator 函数和第三方库如 co
被用来以更同步的方式编写异步代码。
const co = require('co'); // 需要安装 co 库:npm install co
function* generatorFunction() {
yield delayedLog('Hello', 1000);
yield delayedLog('World', 1000);
}
function delayedLog(msg, delay) {
return new Promise(resolve => {
setTimeout(() => {
console.log(msg);
resolve();
}, delay);
});
}
co(generatorFunction());
5. Observable(如 RxJS)
使用 Observable 也是一种处理异步数据流的方法,特别是在处理多个、复杂的异步事件时。RxJS 是一个流行的库,它提供了 Observable 的实现。
const { of } = require('rxjs');
const { delay, concatMap } = require('rxjs/operators');
of('Hello')
.pipe(
delay(1000),
concatMap(x => of(x).pipe(delay(0))), // 仅仅是为了展示 concatMap 的使用
concatMap(x => of(`${x}, World`).pipe(delay(1000)))
)
.subscribe(console.log);
在这个例子中,concatMap
被用来确保每个操作都在前一个操作完成后才执行。RxJS 提供了丰富的操作符来处理各种异步场景。
6. Async/Await 和 for…of 循环
如果你有一个异步操作的数组,并且想要依次执行它们,你可以使用 for...of
循环与 async/await
结合。
async function sequentialExecution(tasks) {
for (const task of tasks) {
await task();
}
}
const tasks = [
() => delayedLog('Hello', 1000),
() => delayedLog('World', 1000)
];
sequentialExecution(tasks);
在这个例子中,sequentialExecution
函数会依次等待每个异步任务完成。tasks
数组包含了返回 Promise 的函数。文章来源:https://www.toymoban.com/news/detail-821598.html
选择哪种方法取决于你的具体需求和你对每种技术的熟悉程度。在现代 JavaScript 开发中,async/await
和 Promise 链通常是最受欢迎的选择,因为它们提供了清晰和简洁的异步代码结构。文章来源地址https://www.toymoban.com/news/detail-821598.html
到了这里,关于Promise的链式调用案例讲解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!