JavaScript生成器,如何理解await和yield的异步组合?
发布于 作者:苏南大叔 来源:程序如此灵动~
当关键字yield碰到关键字await,那么function*必然会遇到关键字async,它们并不冲突,并且可以合理组合到一起。本文就将通过“异步函数生成器”的使用方式,做一些简要探讨。

苏南大叔的“程序如此灵动”博客,记录苏南大叔的代码编程经验总结。本文测试环境:nodejs@20.18.0,chrome@131.0.6778.205。在浏览器或者纯node环境下,都可以运行本文的“异步函数生成器”。
准备工作
主要包括两部分:express代码,await理论。
配套express代码
为了演示异步函数处理,这里使用express临时搭建一个接口处理函数。代码如下:
const express = require("express");
const app = express();
var cors = require("cors");
app.use(cors());
app.all("*", (req, res) => {
let ret = {
url: req.url,
time: Date.now().toString().slice(-6),
};
res.send(JSON.stringify(ret));
});
const server = app.listen(process.env.PORT || 3222, () => {
const port = server.address().port;
console.log("http://localhost:%d", port);
});await返回值
最典型的异步处理,就是fetch()一个接口了。那么,对于await fetch()来说,它的返回值,先后是promise=>reponse=>json string=>json object。似乎在以往的代码里面,都遇到过。
await的返回值是什么类型,并不决定于await关键字,也不决定于fetch()函数。而是决定于fetch()后面的.then()和catch()。因为这个promise()的链式处理,返回什么样的值都是可能的。
(async () => {
let api = "http://localhost:3222/s/n";
const r1 = fetch(api);
// Promise { <pending> }
const r2 = await fetch(api);
// Response { status: 200, ... }
const r3 = await fetch(api).then((res) => res);
// Response { status: 200, ... }
const r4 = await fetch(api).then((res) => res).then((res) => res.json())
// { url: '/s/n', time: '581603' }
})();异步函数和生成器
下面的两个代码,看起来非常类似。仅仅是关键字yield和await的相互替换。但是,两者的结果走向完全不同。
async function fetchData() {
const response = await fetch("http://localhost:3222/s/n");
return await response.json();
}
fetchData().then((data) => {
console.log(data); // { url: '/s/n', time: '892693' }
});相同的效果,因为这个response = yield fetch()的返回值导出又导入的特殊设定,导致多走很多冤枉路。
function* fetchData2() {
const response = yield fetch("http://localhost:3222/ss/nn");
yield response.json();
}
const gen = fetchData2();
const promise_1 = gen.next().value;
promise_1.then((response) => {
const promise_2 = gen.next(response).value; // 这里又传回去了
promise_2.then((data) => {
console.log(data); // { url: '/s/n', time: '892693' }
});
});异步函数生成器【出乎意料】
本段代码就是异步函数生成器了,上面的代码的综合体。async function* + yield await。所以,新的代码逻辑下,又会是什么样的逻辑走向呢?
async function* fetchData3() {
const response = yield await fetch("http://localhost:3222/ss/nn");
yield await response.json();
}
const gen = fetchData3();
const promise_1 = gen.next(); // 这里写法不同
promise_1.then((response) => {
const promise_2 = gen.next(response.value); // 这里又传回去了,注意这里写法不同
promise_2.then((data) => {
console.log(data.value); // { url: '/s/n', time: '892693' },这里写法不同
});
});这段代码,又又又打破认知了!!!
用了很久才接受的yield的返回值是{value:'',done:false}这事儿,在async function*里面,变成了要返回的是就是value(没有外部套着的那些)。然后再.then()一下,才会变成认知中的{value:'',done:false}的格式。
也就是说yield的概念再次分裂了!

没有await的异步生成器
上面代码里,如果把【await关键字全部去掉】,保持async字样的话,上面的代码结论依然成立。
async function* fetchData3() {
const response = yield fetch("http://localhost:3222/ss/nn");
yield response.json();
}
const gen = fetchData3();
const promise_1 = gen.next(); // 这里写法不同
promise_1.then((response) => {
const promise_2 = gen.next(response.value); // 这里又传回去了,注意这里写法不同
promise_2.then((data) => {
console.log(data.value); // { url: '/s/n', time: '892693' },这里写法不同
});
});相关文章
- https://newsn.net/say/js-yield-await.html
- https://newsn.net/say/js-yield-star.html
- https://newsn.net/say/js-yield-2.html
- https://newsn.net/say/js-yield.html