接前面的:
- JavaScript引擎与运行时入门小记:在哪些环境下可以运行JavaScript
- JavaScript学习笔记(一):理解概念,一等函数、编程范式、原型、单线程
- JavaScript学习笔记(二):全局对象globalThis,this用法,全局对象们
- JavaScript学习笔记(三):7种原始数据类型,对象类型(引用类型),typeof,原始类型包装器
- JavaScript学习笔记(四):ES6模块ESM,ES6之前CJS,Nodejs如何区分传统脚本与ESM模块文件
- JavaScript学习笔记(五):Event loop 简简单单一个东西,术语太乱了
- JavaScript学习笔记(六):Promise 基本概念,一些简单例子
- JavaScript学习笔记(七):生成器、异步函数、异步迭代器
继续学习JavaScript。通过和Python对比方式:
- 看看JavaScript中的4种函数
- 对比看看生成器函数,基本无差别
- 对比看看异步函数,和Python类似,但底层差异很大
- 对比看看异步生成器函数
Python to JavaScript?
在Python 协程小记(一)中,我们了解Python中的5种函数:
import asyncio
import types
def func1():
print('function')
def func2():
print('generator')
yield
async def func3():
print('coroutine')
await asyncio.sleep(1)
async def func4():
print('async generator')
yield
@types.coroutine
def func5():
print('generator-based coroutine')
yield
# 以上函数调用方式各异
async def main():
func1()
f2 = func2(); next(f2)
await func3()
f4 = func4(); await anext(f4)
await func5()
asyncio.run(main())
那么在JavaScript中,和python能对应上吗?
function func1() {
console.log('function');
}
function* func2() {
console.log('generator');
yield;
}
async function func3() {
console.log('coroutine');
await new Promise((resolve) => setTimeout(resolve, 1000));
}
async function* func4() {
console.log('async generator');
yield;
}
// 执行方式各异
func1();
const f2 = func2();
f2.next();
await func3();
const f4 = func4();
await f4.next();
看起来不错,除了没有Python自己折腾的@types.coroutine
,基本一一对应。
不过:
- JavaScript中的 async 是基于Promise的语法糖,Python中的 async 不是。
- JavaScript中的 生成器使用
function*
与普通函数区分,Python只依赖其内部是否有yield。
JavaScript四种函数
这样更直观一些:
function func1() {
}
function* func2() {
yield;
}
async function func3() {
await new Promise((resolve) => setTimeout(resolve, 1000));
}
async function* func4() {
yield;
}
console.log(func1) // [Function: func1]
console.log(func2) // [GeneratorFunction: func2]
console.log(func3) // [AsyncFunction: func3]
console.log(func4) // [AsyncGeneratorFunction: func4]
或者这样
function func1() {}
function* func2() {}
async function func3() {}
async function* func4() {}
console.log(func1) // [Function: func1]
console.log(func2) // [GeneratorFunction: func2]
console.log(func3) // [AsyncFunction: func3]
console.log(func4) // [AsyncGeneratorFunction: func4]
这四种函数对象各不相同:
- 函数
- 生成器函数
- 异步函数
- 异步生成器函数
它们的返回值,也可以简单看一下:
- 异步函数的返回值是 Promise!(不同于Python下称呼的 coroutine)
function func1() {}
function* func2() {}
async function func3() {}
async function* func4() {}
console.log(func1()) // undefined
console.log(func2()) // Object [Generator] {}
console.log(func3()) // Promise { undefined }
console.log(func4()) // Object [AsyncGenerator] {}
注:
- yield 在生成器或异步生成器函数内使用
- await 在异步函数和异步生成器函数内使用。(严格模式下或模块中,还可以用顶级 await)
在标准化之前,生成器也有不用
function*
而不是直接判定function
内有无yield
的用法。在标准化之前,也有是否使用 async,还是只判定的await的提案。
生成器函数
这个也可以和Python生成器与yield进行对比。
Python vs Javascript 例子
Python下生成器函数:
def my_generator():
yield 1
yield 2
yield 3
# use the generator
gen = my_generator()
print(next(gen)) # 1
print(next(gen)) # 2
print(next(gen)) # 3
JavaScript生成器函数:
function* myGenerator() {
yield 1;
yield 2;
yield 3;
}
// use the generator
const gen = myGenerator();
console.log(gen.next().value); // 1
console.log(gen.next().value); // 2
console.log(gen.next().value); // 3
用起来很像。
生成器函数 与 生成器
function*
定义的是生成器函数(- 函数返回值是 生成器(Generator)
function* myGenerator() {
yield 1;
yield 2;
yield 3;
}
// use the generator
const gen = myGenerator();
console.log(myGenerator); // [GeneratorFunction: myGenerator]
console.log(gen); // Object [Generator] {}
- 生成器实现了可迭代协议(iterable protocol)与迭代器协议(iterator protocol)。
// use the generator
const gen = myGenerator();
for (const value of gen) {
console.log(value);
}
对比 Python
对于生成器进行迭代,JavaScript下的for... of
和Python下的for in
用法也很类似:
def my_generator():
yield 1
yield 2
yield 3
# use the generator
gen = my_generator()
for value in gen:
print(value)
yield
- javascript下区分
yield
与yield*
,后者用于代理其他迭代器:
function* func1() {
yield "1+1=10";
}
function* func2() {
yield* func1();
}
const iterator = func2();
console.log(iterator.next().value); // 1+1=10
- 就如同 python下区分
yield
与yield from
一样
def func1():
yield "1+1=10"
def func2():
yield from func1()
iterator = func2()
print(next(iterator)) # 1+1=10
可迭代 与 迭代器
- 可迭代对象需要实现
@@iterator
属性,该属性用于返回迭代器。 - 迭代器需要实现
next()
方法,该方法可以接受无参数或一个参数(可类比Python下的send函数)
ECMAScript 引入 Symbol后,可以使用
[Symbol.iterator]
替代 字符串@@iterator
写法。
前面生成器函数可用于生成可迭代对象。
不用生成器函数,也可以手动实现可迭代协议:
// 手动实现可迭代协议的对象
const myIterable = {
data: [1, 2, 3],
[Symbol.iterator]: function() {
let index = 0;
return {
next: () => {
if (index < this.data.length) {
return { value: this.data[index++], done: false };
} else {
return { value: undefined, done: true };
}
}
};
}
};
// use for...of
for (const value of myIterable) {
console.log(value);
}
不直接创建对象,用class是否更面向对象一些?
class MyIterable {
constructor() {
this.data = [1, 2, 3];
}
// 实现可迭代协议的方法
[Symbol.iterator]() {
let index = 0;
return {
next: () => {
if (index < this.data.length) {
return { value: this.data[index++], done: false };
} else {
return { value: undefined, done: true };
}
}
};
}
}
// use for...of
const myIterable = new MyIterable();
for (const value of myIterable) {
console.log(value);
}
还能这么写?:
const myIterable = {};
myIterable[Symbol.iterator] = function* () {
yield 1;
yield 2;
yield 3;
};
// use for...of
for (const value of myIterable) {
console.log(value);
}
异步函数
async的例子很简单
例子
async function func() {
await Promise.resolve('1+1=10');
return '1+1=2';
}
console.log(func) // [AsyncFunction: func]
console.log(func()) // Promise { <pending> }
console.log(await func()) // 1+1=2
但,不妨先和Python对比一下,
异步函数、协程函数
- | JavaScript | Python |
---|---|---|
async | 异步函数,async function f(){} | 协程函数, async def f(): |
函数返回值 | Promise对象,Thenable | 协程对象,Awaitable |
使用方式 | await 关键字用于等待异步操作完成 |
await 关键字用于等待协程完成 |
再试着,放个图看看:
不同于JavaScript和Python在生成器方面非常类似,它们在Promise异步函数和协程函数返回值上,差别还是很大的。但是形式上都可以配合 await
使用。
JS Promise 与 Python Future?
Javascript中的Promise 与 Python 中的 asyncio.Future 在概念上,有些类似。
在Python中,可以把一个协程转换成 Future,而后使用 await 来等待它完成:
import asyncio
async def async_operation():
await asyncio.sleep(1)
return "1+1=10"
async def main():
# convert async operation to future
future = asyncio.ensure_future(async_operation())
result = await future
print(result)
asyncio.run(main())
对应 Javascript代码:
async function asyncOperation() {
await new Promise(resolve => setTimeout(resolve, 1000));
return "1+1=10";
}
async function main() {
const promise = asyncOperation();
const result = await promise;
console.log(result);
}
main();
注:
* JavaScript中,Promise
是由异步函数返回的一个对象,它表示操作的当前状态。在将Promise返回给调用者的时候,操作通常尚未完成,但是Promise对象提供了方法来响应其最终成功或失败的场景。
* Python中,asyncio.Future
是 asyncio
模块中用于表示异步操作结果的类。它表示一个可能尚未完成的异步操作,可以在将来的某个时刻完成,并且在完成后可以包含一个结果或一个异常。
二者都提供了方式来设置目标值,或者没有目标值时设置错误原因(异常)。
await
在 JavaScript 中,await
关键字用于等待一个 Promise 对象的完成。当使用 await
时,会发生:
- 暂停执行: 当
await
遇到时,它会暂停当前async
函数的执行,但不会阻塞整个程序或浏览器页面。 - 让出执行线程:
await
会让出执行线程,使得事件循环可以处理其他任务。这是 JavaScript 中单线程执行的一个关键特性,允许异步操作不会阻塞程序的其他部分。 - 等待 Promise 解决(完成):
await
表达式后面通常是一个返回 Promise 对象的异步操作。它等待这个 Promise 对象的状态变为 resolved(解决)或 rejected(拒绝)。 - 恢复执行: 一旦 Promise 对象被解决,
await
表达式返回解决值,并且async
函数继续执行。
异步迭代器
字面理解,就是异步函数和迭代器的复合了,例子挺直观的:
async function* asyncGenerator() {
yield new Promise(resolve => setTimeout(() => resolve("1+1=10"), 1000));
yield new Promise(resolve => setTimeout(() => resolve("1+1=2"), 1000));
yield new Promise(resolve => setTimeout(() => resolve("1+1!=3"), 1000));
}
const asyncIterable = asyncGenerator();
for await (const value of asyncIterable) {
console.log(value);
}
// output:
// 1+1=10
// 1+1=2
// 1+1!=3
用Python改写的话,也不复杂:
import asyncio
async def async_generator():
await asyncio.sleep(1)
yield "1+1=10"
await asyncio.sleep(1)
yield "1+1=2"
await asyncio.sleep(1)
yield "1+1!=3"
async def main():
async_iterable = async_generator()
async for value in async_iterable:
print(value)
asyncio.run(main())
# output:
# 1+1=10
# 1+1=2
# 1+1!=3
二者还是挺像的,不过:
- JavaScript使用
for await ... of
- Python使用
async for ... in
参考
- https://262.ecma-international.org/8.0/#sec-async-function-definitions
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*
- QTBUG-58620 Add async/await support to V4
- https://dev.to/3shain/promise-coroutine-548i