三种语法功能放在一起,是因为他们都有相似特点:
本文重点回答以下几个问题:
generator 是生成器,从生成器的行为来看,它就是一个迭代器。
function* foo(end) {
var idx = 0;
while (idx < end) {
yield idx; // 保存当前状态并返回 当前idx
idx += 5;
}
}
const iterator = foo(20);
// iterator.next().value 手动迭代
for (let val of iterator) { // 输出 0 到 19
console.log(val);
}
对应到生成器函数中,他会在 yield
的时候保存当前函数的状态。那么,函数的状态保存在哪? 习惯了 C 函数的我们很难想象如何将状态保存在函数中。可能协程和 setjmp/longjmp 可以辅助我们思考生成器的实现,但还是不能直接对应到生成器函数的行为。比如:生成器需要记录如下信息
想在 c 语言中保存上述信息是不容易的。是因为函数在 js
是第一类值,上述的信息便可以保存在函数值本身内。对应quickjs 相当于将 pc 指针 和 参数 信息,local_buf,保存在对应的函数对象 (实际上保存在 StackFrame 中) 内,而 cur_ref 信息 quickjs 本身就已经保存了。而 c/c++ 语言的函数没有类似功能。
function* inner(index) {
var i = 0;
while (i < 5) {
yield index + i;
i++;
}
}
// 错误的生成器用法,但是启发我们思考,这样的用法,是不是很像 await?
// function* foo(index) {
// while (index < 20) {
// yield inner(index);
// index += 5;
// }
// }
// 正确版本
function* foo1(index) {
while (index < 20) {
var it = inner(index);
for (let val of it) {
yield val;
}
index += 5;
}
}
// 语法糖版本
function* foo2(index) {
while (index < 20) {
yield *inner(index);
index += 5;
}
}
const iterator = foo1(0);
for (let val of iterator) {
console.log(val);
}
Async 的代码执行流程是下面这棵树的中序遍历
Async 函数的代码执行流程如下
Async 函数的代码执行流程非常像 树形 generator
Async 函数框架
async function asyncfunc() {
code1;
var a1 = await asyncfunc1(); // 这里是一个 async 函数
code2;
var a2 = await promise1; // 这里是一个 真正的 Promise
code3;
var a3 = await asyncfunc2(); // 这里是一个 async 函数
codeR;
return x;
}
asyncfunc();
async function wrapFetch(url) {
console.log("wrapFetch code1");
const response = await fetch(url);
console.log("wrapFetch code2");
return response;
}
// 异步函数示例
async function fetchAsyncData() {
console.log("fetchAsyncData code1");
const response = await wrapFetch('https://qqlykm.cn/api/weather/get');
console.log("fetchAsyncData code2 ok?", response.ok);
const data = await response.json();
console.log(data);
}
// 调用异步函数
console.log("before async");
fetchAsyncData();
console.log("after async");
promise
(叶子节点是真正的 promise),代码的执行就要遵循 promise 的执行规则,而 promise 本身就是异步的,因此无法串行化。想要理解如何将 async 函数返回的到底是什么 promise?以及如何将 async 函数翻译成一个 promise 的形式,需要深入理解 then 函数。
const myPromise = new Promise((resolve, reject) => {
// 进行异步操作
const randomNumber = Math.random();
// 如果操作成功,调用resolve并传递结果
resolve(randomNumber);
});
// 使用Promise对象
myPromise.then(result => {
// 操作成功时的处理逻辑
console.log("操作成功,结果为:" + result);
**return 1; **
**return new Promise((rs, rj) => rs(2));**
}).then( val => {
console.log(val);
})
如何改写?下面用一个示例来展示
注:这里并没有关注异常执行流,而是只关注异步执行流。异常执行流要想完全转换,需要在promise里注册错误回调,并且 async 和 promise 都要捕获异常
// async 写法
async function asyncfunc() {
code1;
var a1 = await asyncfunc1(); // 这里是一个 async 函数
code2;
var a2 = await promise1; // 这里是一个 真正的 Promise
code3;
var a3 = await asyncfunc2(); // 这里是一个 async 函数
codeR;
return x;
}
asyncfunc();
// 等价的promise 写法
function func() {
return new Promise((resolve, reject) =>{
code1;
func1()
.then( (val) => resolve(val) );
}).then((a1) => {
code2;
return promise1;
}).then((a2)=> {
code3;
// 返回一个Promise
return func2();
}).then((a3) => {
codeR;
return x;
});
}
func();
异步调用一个 网络 api 接口
async function wrapFetch(url) {
console.log("wrapFetch code1");
const response = await fetch(url);
console.log("wrapFetch code2");
return response;
}
// 异步函数示例
async function fetchAsyncData() {
console.log("fetchAsyncData code1");
const response = await wrapFetch('https://qqlykm.cn/api/weather/get');
console.log("fetchAsyncData code2 ok?", response.ok);
const data = await response.json();
console.log(data);
}
// 调用异步函数
console.log("before async");
fetchAsyncData();
console.log("after async");
function wrapFetch(url) {
return new Promise( function (resolve, reject) {
console.log("wrapFetch code1");
fetch(url).then( (response) => {
resolve(response);
console.log("wrapFetch code2");
})
});
}
function fetchAsyncData() {
return new Promise(function (resolve, reject) {
console.log("fetchAsyncData code1");
wrapFetch('https://qqlykm.cn/api/weather/get')
.then( val => resolve(val) )
})
.then( (response) => {
console.log("fetchAsyncData code2 ok?", response.ok);
return response.json()
})
.then((data) => {
console.log(data);
});
}
// 调用异步函数
console.log("before async");
fetchAsyncData();
console.log("after async");