接前面的:
继续学习JavaScript。通过和Python对比方式:
- 看看JavaScript中的4种函数
- 对比看看生成器函数,基本无差别
- 对比看看异步函数,和Python类似,但底层差异很大
- 对比看看异步生成器函数
Python to JavaScript?
在Python 协程小记(一)中,我们了解Python中的5种函数:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32 | 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能对应上吗?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 | 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四种函数
这样更直观一些:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 | 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
写法。
前面生成器函数可用于生成可迭代对象。
不用生成器函数,也可以手动实现可迭代协议:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 | // 手动实现可迭代协议的对象
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是否更面向对象一些?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 | 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);
}
|
还能这么写?:
1
2
3
4
5
6
7
8
9
10
11
12 | 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 来等待它完成:
1
2
3
4
5
6
7
8
9
10
11
12
13 | 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代码:
1
2
3
4
5
6
7
8
9
10
11
12 | 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
函数继续执行。
异步迭代器
字面理解,就是异步函数和迭代器的复合了,例子挺直观的:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 | 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改写的话,也不复杂:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 | 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