异步的发展(下)
异步(下)
前言
在现代 JavaScript 编程中,异步编程是必不可少的部分。上篇文章中我们已经讨论了回调和 Promise。在这篇文章中,我们将探讨 Generator 函数和 async/await,并介绍它们如何在异步编程中发挥作用。
Generator函数
Generator 函数是 ES6 引入的一种新的函数形式,它允许函数在执行过程中暂停,并在之后恢复执行。与普通函数不同,Generator 函数会返回一个迭代器对象,通过这个迭代器对象的 next()
方法,可以逐步执行函数的代码。
Generator 函数使用 function*
关键字定义,函数内部使用 yield
关键字来暂停执行。
下面看一个简单的示例:
function* gen() {
yield 1;
yield 2;
yield 3;
}
const g = gen();
console.log(g.next()); // { value: 1, done: false }
console.log(g.next()); // { value: 2, done: false }
console.log(g.next()); // { value: 3, done: false }
console.log(g.next()); // { value: undefined, done: true }
调用 Generator 函数返回的迭代器对象的 next()
方法时,会返回一个对象,该对象包含两个属性:
value
:当前yield
表达式的值。done
:一个布尔值,表示 Generator 函数是否已执行完毕。
值得注意的是
value
是指当前yield
表达式的值,这是什么意思呢,是执行yield
返回值的结果嘛?
我们来看下面这个代码,我们用变量a,b承接yield的返回值并打印
function* gen() {
let a = yield 1;
console.log(a);
let b = yield 2;
console.log(b);
}
const g = gen();
console.log(g.next());
console.log(g.next("这是打印a的结果"));
console.log(g.next("这是打印b的结果"));
结果:
{ value: 1, done: false }
这是打印a的结果
{ value: 2, done: false }
这是打印b的结果
{ value: undefined, done: true }
从这个代码中,我们可以知道
-
yield
表达式的值:当 Generator 函数执行到yield
表达式时,它会暂停执行,并将yield
表达式后面的值作为当前的value
返回。直到下一次调用next()
方法时,Generator 函数才会继续执行。 -
yield
语句的返回值:在yield
表达式的返回值是调用next()
方法时传入的参数。
那么通过这种语法特点是如何实现异步的
要知道Generator 函数本身是同步的,但它们可以与 yield
关键字和一些控制逻辑结合,来实现异步操作的顺序执行。通过结合 yield
和 Promise
,可以在 Generator 函数中处理异步操作。
下面是一个示例:
function A() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log("异步a");
resolve();
}, 1000);
});
}
function B() {
console.log("同步b");
}
function* gen() {
yield A();
yield B();
}
function runGenerator(gen) {
const iterator = gen();
function handleResult(result) {
if (result.done) return;
const value = result.value;
if (value instanceof Promise) {
value.then(res => handleResult(iterator.next(res)))
.catch(err => iterator.throw(err));
} else {
handleResult(iterator.next(value));
}
}
handleResult(iterator.next());
}
runGenerator(gen);
// 结果:
// 异步a
// 同步b
在这个示例里:
-
定义一个辅助函数
runGenerator
,它接收一个生成器函数gen
作为参数。 -
创建生成器对象
iterator
并启动生成器函数。 -
定义一个内部函数
handleResult
来处理生成器的执行结果。 -
检查生成器返回的结果是否完成 (
result.done
)。 -
如果生成器返回一个
Promise
,等待其解决并递归调用handleResult
继续执行生成器。 -
如果生成器返回一个非
Promise
值,直接递归调用handleResult
继续执行生成器。
通过这种方式,你可以让生成器函数与异步操作一起工作,实现异步控制流。
但是使用生成器函数和辅助函数来处理异步操作的缺点:
-
复杂性高:这种方法需要额外的辅助函数来处理生成器的执行结果,使得代码的复杂性增加。
-
错误处理麻烦:虽然可以在辅助函数中捕获错误并抛出,但处理起来并不直观,特别是在复杂的异步流程中。
基于上述暂停函数执行的想法,就有了async/await这种方式,对于它的内部实现逻辑就是与Generator 函数有关,有兴趣的小伙伴可以阅读源码进行复刻。
async/await
async/await
是 ES2017 引入的语法,用于简化基于 Promise 的异步编程。async
关键字用于定义一个异步函数,该函数会隐式地返回一个 Promise。await
关键字用于暂停异步函数的执行,等待一个 Promise 解析,并返回解析值。
function fetchData(url) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(`Data from ${url}`);
}, 1000);
});
}
async function getData() {
try {
const data1 = await fetchData('url1');
console.log(data1);
const data2 = await fetchData('url2');
console.log(data2);
} catch (error) {
console.error('Error:', error);
}
}
getData();
// Data from url1
// Data from url2
fetchData
函数返回一个 Promise
,模拟异步数据获取。getData
是一个异步函数,使用 await
暂停执行,等待 fetchData
的 Promise 解析,并打印获取的数据。
最后
异步发展至今,async/await已经是非常优雅的方式了,在实际编程实践中,我们肯定也是直接用async/await来实现异步操作啦。
转载自:https://juejin.cn/post/7397028381960847411