Generator 生成器函数
Generator 是一个生成遍历器对象的函数 同时它也是一个状态机。通过它返回的遍历器对象,可以依次遍历 Generator 函数内部的每一个状态
语法
// function关键字和函数名之间有一个星号
function* helloWorldGenerator() {
// 函数体内部通过yield表达式定义不同的内部状态
yield 'hello';
yield 'world';
return 'ending';
}
const hw = helloWorldGenerator();
使用
和普通函数调用一样。不同的是,Generator的返回值是一个迭代器
- 调用 Generator 函数,返回一个遍历器对象,代表 Generator 函数的内部指针。
- 以后,每次调用遍历器对象的
next方法,就会返回一个有着value和done两个属性的对象。 value属性表示当前的内部状态的值,是yield表达式后面那个表达式的值;done属性是一个布尔值,表示是否遍历结束
hw.next()
// Generator 函数开始执行,直到遇到第一个yield表达式为止
// { value: 'hello', done: false }
hw.next()
// Generator 函数从上次yield表达式停下的地方,一直执行到下一个yield表达式
// { value: 'world', done: false }
hw.next()
// Generator 函数从上次yield表达式停下的地方,一直执行到return语句(如果没有return语句,就执行到函数结束)
// { value: 'ending', done: true }
hw.next()
// Generator 函数已经运行完毕,next方法返回对象的value属性为undefined,done属性为true。以后再调用next方法,返回的都是这个值
// { value: undefined, done: true }
生成器的另一种用法是:内部不使用yield表达式,仅作为一个惰性执行函数
function* f() {
console.log('执行了!')
}
// 这里只是调用f获得了一个迭代器,此时console.log不会执行
const generator = f();
// 只有调用next,console.log才会执行
generator.next()
迭代器 Iterator与yield表达式
迭代器可以理解成一个有next方法的对象,通过迭代器不断的调用next方法进行遍历状态。在JS中迭代器借助yield实现,yield表达式可以暂停函数的执行。
遍历器对象的next方法的运行逻辑
(1)遇到yield表达式,就暂停执行后面的操作,并将紧跟在yield后面的那个表达式的值,作为返回的对象的value属性值。
(2)下一次调用next方法时,再继续往下执行,直到遇到下一个yield表达式。
(3)如果没有再遇到新的yield表达式,就一直运行到函数结束,直到return语句为止,并将return语句后面的表达式的值,作为返回的对象的value属性值。
(4)如果该函数没有return语句,则返回的对象的value属性值为undefined。
需要注意的是,yield表达式后面的表达式,只有当调用next方法、内部指针指向该语句时才会执行
对象的Symbol.iterator
任意一个对象的 Symbol.iterator方法,等于该对象的遍历器生成函数,调用该函数会返回该对象的一个遍历器对象
Generator 函数就是遍历器生成函数,因此可以把 Generator 赋值给对象的Symbol.iterator属性
const myIterable = {};
myIterable[Symbol.iterator] = function* () {
yield 1;
yield 2;
yield 3;
};
[...myIterable] // [1, 2, 3]
迭代器的Symbol.iterator
Generator 函数执行后,返回一个遍历器对象。该对象本身也具有Symbol.iterator属性,执行后返回自身
function* gen(){
// some code
}
const g = gen();
g[Symbol.iterator]() === g
// true
迭代器的next()、throw()、return()
迭代器对象除了next方法,还有throw,return方法,它们的作用都是让 Generator 函数恢复执行,并且使用不同的语句替换yield表达式
next
next()是将yield表达式替换成一个值
const g = function* (x, y) {
let result = yield x + y;
return result;
};
const gen = g(1, 2);
gen.next(); // Object {value: 3, done: false}
gen.next(1); // Object {value: 1, done: true}
// 相当于将 let result = yield x + y
// 替换成 let result = 1;
throw
throw()是将yield表达式替换成一个throw语句
gen.throw(new Error('出错了')); // Uncaught Error: 出错了
// 相当于将 let result = yield x + y
// 替换成 let result = throw(new Error('出错了'));
return
return()是将yield表达式替换成一个return语句
gen.return(2); // Object {value: 2, done: true}
// 相当于将 let result = yield x + y
// 替换成 let result = return 2;
yield* 表达式
用来在一个 Generator 函数里面执行另一个 Generator 函数。这个Generator函数即可以是同步的也可以是异步的
function* foo() {
yield 'a';
yield 'b';
}
function* bar() {
yield 'x';
yield* foo();
yield 'y';
}
// 等同于
function* bar() {
yield 'x';
yield 'a';
yield 'b';
yield 'y';
}
// 等同于
function* bar() {
yield 'x';
for (let v of foo()) {
yield v;
}
yield 'y';
}
for (let v of bar()){
console.log(v);
}
// "x"
// "a"
// "b"
// "y"
异步遍历器
异步遍历器的最大的语法特点,就是调用遍历器的next方法,返回的是一个 Promise 对象
一个对象的同步遍历器的接口,部署在**Symbol.iterator属性上面。同样地,对象的异步遍历器接口,部署在Symbol.asyncIterator**属性上面
语法
异步遍历器其实返回了两次值。第一次调用的时候,返回一个 Promise 对象;等到 Promise 对象resolve了,再返回一个表示当前数据成员信息的对象。
这就是说,异步遍历器与同步遍历器最终行为是一致的,只是会先返回 Promise 对象,作为中介
const asyncIterable = createAsyncIterable(['a', 'b']);
const asyncIterator = asyncIterable[Symbol.asyncIterator]();
asyncIterator
.next()
.then(iterResult1 => {
console.log(iterResult1); // { value: 'a', done: false }
return asyncIterator.next();
})
.then(iterResult2 => {
console.log(iterResult2); // { value: 'b', done: false }
return asyncIterator.next();
})
.then(iterResult3 => {
console.log(iterResult3); // { value: undefined, done: true }
});
使用for await of遍历异步迭代器
for...of循环用于遍历同步的 Iterator 接口。for await...of循环,则是用于遍历异步的 Iterator 接口
async function f() {
// createAsyncIterable()返回一个拥有异步遍历器接口的对象
// for...of循环自动调用这个对象的异步遍历器的next方法,会得到一个 Promise 对象
// await用来处理这个 Promise 对象,一旦resolve,就把得到的值(x)传入for...of的循环体
for await (const x of createAsyncIterable(['a', 'b'])) {
console.log(x);
}
}
// a
// b
异步Generator
异步 Generator 函数的作用,是返回一个异步遍历器对象
async function* gen() {
yield 'hello';
}
// gen是一个异步 Generator 函数,执行后返回一个异步 Iterator 对象
const genObj = gen();
// 对该对象调用next方法,返回一个 Promise 对象
genObj.next().then(x => console.log(x));
// { value: 'hello', done: false }
dart中的迭代器一撇
dart中也有生成器一说,语法和JS很类似,我们就简单看一下就行
// 同步生成器的返回值是Iterable,函数体要使用sync*修饰
Iterable<int> naturalsTo(int n) sync* {
int k = 0;
// 生成器分为同步生成器和异步生成器,函数内部使用yield生成值
while (k < n) yield k++;
}
// 生成器使用时必须调用next()方法
var gen = naturalsTo(3)
gen.next()
// 异步生成器的返回值是Stream,函数体要使用async*修饰
Stream<int> asynchronousNaturalsTo(int n) async* {
int k = 0;
while (k < n) yield k++;
}
// 递归语法的生成器
// 如果生成器是递归语法写的,使用yield* 可以提高效率
Iterable<int> naturalsDownFrom(int n) sync* {
if (n > 0) {
yield n;
yield* naturalsDownFrom(n - 1);
}
}
Promise
Promise是一个对象,用于封装一个将来发生的操作(或者叫异步操作),这个操作要么完成时返回结果值或者要么失败时返回失败原因
Promise的状态
可以把Promise理解成是一个状态机,它内部有三个状态
pending:初始态
fulfilled:成功时的状态
rejected:失败时的状态
Promise的特性
- Promise只会由pending转换到另外两个状态之一,不会逆向转换。并且转换只会发生一次。
- Promise的状态只能由Promise内部修改,外部无法修改
- 无法取消Promise,创建一个Promise时,参数内的代码会立即执行
- 一旦Promise转换成fulfilled或rejected状态,这个promise就叫做被settled。被settled之后的promise,如果调用then或finally方法,这些方法传入的回调函数会立即放入微任务队列等待执行
- 同一个Promise多次调用then、finally方法,内部会将对应的回调存储到一个数组中。待状态变化时遍历数组调用回调函数
- 可以将catch方法看成是then(null, rejection)或者then(undefined,rejection)的别名,相当于一个只指定了第二个参数的then方法
- 如果没有使用catch指定错误处理的回调函数,Promise对象抛出错误的时候就不会传递到外层代码。即不会有任何反应。其原因在于,在Promise使用try catch捕获了错误,然后传递到了reject回调里,没有抛出到外面
下图展示了Promise状态转换的示意
链式调用
Promise的then、catch、finally都会返回一个新的Promise,新的Promise还可以继续调用then、catch、finally再返回新的Promise,从而通过一个个新的Promise完成链式调用
async、await
async/await实际上是对Generator(生成器)的封装,是一个语法糖
async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果
async
async表示一个函数是异步的,会返回一个 Promise 对象,可以使用then方法添加回调函数。当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句
async函数有多种形式
// 函数声明
async function foo() {}
// 函数表达式
const foo = async function () {};
// 对象的方法
let obj = { async foo() {} };
obj.foo().then(...)
// Class 的方法
class Storage {
constructor() {
this.cachePromise = caches.open('avatars');
}
async getAvatar(name) {
const cache = await this.cachePromise;
return cache.match(`/avatars/${name}.jpg`);
}
}
const storage = new Storage();
storage.getAvatar('jake').then(…);
// 箭头函数
const foo = async () => {};
细节
async函数返回一个 Promise 对象。async函数内部return语句返回的值,会成为then方法回调函数的参数
如果在函数中return的不是一个promise,那么将等同于使用Promise.resolve(x)给包装起来。这其中也包括不写return时返回的undefined
async function f() {
return 'hello world';
}
f().then(v => console.log(v))
// "hello world"
async函数内部抛出错误,会导致返回的 Promise 对象变为reject状态。抛出的错误对象会被catch方法回调函数接收到
async function f() {
throw new Error('出错了');
}
f().then(
v => console.log('resolve', v),
e => console.log('reject', e)
)
//reject Error: 出错了
只有async函数内部的异步操作执行完,才会执行then方法指定的回调函数
async function getTitle(url) {
// 等待fetch方法产生的promise完成,才会执行response.next
let response = await fetch(url);
// 等待response.next方法执行完成,才会执行html.match
let html = await response.text();
return html.match(/<title>([\s\S]+)<\/title>/i)[1];
}
getTitle('https://tc39.github.io/ecma262/').then(console.log)
// "ECMAScript 2017 Language Specification"
实现原理
async 函数的实现原理,就是将 Generator 函数和自动执行器,包装在一个函数里
async function fn(args) {
// ...
}
// 等同于
function fn(args) {
return spawn(function* () {
yield step1
yield step2
yield step3
. . .
});
}
await
await命令后面如果是一个 Promise 对象,会返回该对象的结果。如果不是 Promise 对象,会直接返回对应的值
只能在async函数内部使用
任何一个await语句后面的 Promise 对象变为reject状态,那么整个async函数都会中断执行
async function f() {
await Promise.reject('出错了');
await Promise.resolve('hello world'); // 不会执行
}
在鸿蒙中的实现
await promise最终会被转化成一个函数 gitee.com/openharmony…
EtsObject *EtsAwaitPromise(EtsPromise *promise)
从代码中我们也能够知道,在鸿蒙底层await是通过协程实现的
await会一直等待它后面promise的状态变成resolved或者rejected。如果它后面的promise一直不变状态,await下面的代码就一直不会执行,这点需要特别注意
鸿蒙中Promise的实现
Promise A+规范
英文原版 promisesaplus.com/
中文翻译 www.ituring.com.cn/article/665…
Promise/A+ 规范中定义的 Promise 的基本特征:
- promise 有三个状态:
pending,fulfilled,orrejected;「规范 Promise/A+ 2.1」 new promise时, 需要传递一个executor()执行器,执行器立即执行;executor接受两个参数,分别是resolve和reject;- promise 的 默认状态是
pending; - promise 有一个**
value保存成功状态的值**,可以是undefined/thenable/promise;「规范 Promise/A+ 1.3」 - promise 有一个**
reason保存失败状态的值**;「规范 Promise/A+ 1.5」 - promise 只能从
pending到rejected, 或者从pending到fulfilled,状态一旦确认,就不会再改变; - promise 必须有一个
then方法,then 接收两个参数,分别是 promise 成功的回调 onFulfilled, 和 promise 失败的回调 onRejected;「规范 Promise/A+ 2.2」 - 如果调用 then 时,promise 已经成功,则执行
onFulfilled,参数是promise的value; - 如果调用 then 时,promise 已经失败,那么执行
onRejected, 参数是promise的reason; - 如果 then 中抛出了异常,那么就会把这个异常作为参数,传递给下一个 then 的失败的回调
onRejected; - then 的参数
onFulfilled和onRejected可以缺省,如果onFulfilled或者onRejected不是函数,将其忽略,且依旧可以在下面的 then 中获取到之前返回的值;「规范 Promise/A+ 2.2.1、2.2.1.1、2.2.1.2」 - promise 可以 then 多次,每次执行完 promise.then 方法后返回的都是一个“新的promise" ;「规范 Promise/A+ 2.2.7」
- 如果 then 的返回值 x 是一个普通值,那么就会把这个结果作为参数,传递给下一个 then 的成功的回调中;
- 如果 then 中抛出了异常,那么就会把这个异常作为参数,传递给下一个 then 的失败的回调中;「规范 Promise/A+ 2.2.7.2」
- 如果 then 的返回值 x 是一个 promise,那么会等这个 promise 执行完,promise 如果成功,就走下一个 then 的成功;如果失败,就走下一个 then 的失败;如果抛出异常,就走下一个 then 的失败;「规范 Promise/A+ 2.2.7.3、2.2.7.4」
- 如果 then 的返回值 x 和 promise 是同一个引用对象,造成循环引用,则抛出异常,把异常传递给下一个 then 的失败的回调中;「规范 Promise/A+ 2.3.1」
- 如果 then 的返回值 x 是一个 promise,且 x 同时调用 resolve 函数和 reject 函数,则第一次调用优先,其他所有调用被忽略;「规范 Promise/A+ 2.3.3.3.3」
源码
翻了一下资料,最终在 gitee.com/openharmony… 中发现了鸿蒙中Promise的封装。
/*
* Copyright (c) 2022-2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package std.core;
// 定义Promise的接口类型
export interface PromiseLike<T> {
then<U>(onFulfilled: () => U|PromiseLike<U> throws): PromiseLike<U>;
then<U>(onFulfilled: (value: T) => U|PromiseLike<U> throws): PromiseLike<U>;
// NOTE(audovichenko): Mark parameters by '?' instead of add '|undefined'. Issue #17204
then<U, E = never>(onFulfilled: ((value: T) => U|PromiseLike<U> throws)|undefined,
onRejected: ((error: NullishType) => E|PromiseLike<E> throws)|undefined): PromiseLike<U|E>;
}
// 定义fulfilled状态时的接口类型
export class PromiseFulfilledResult<T> {
status: string;
value: T;
}
// 定义rejected状态时的接口类型
export class PromiseRejectedResult {
status: string;
reason: NullishType;
}
export type PromiseSettledResult<T> = PromiseFulfilledResult<T>|PromiseRejectedResult
/**
* Class represents a result of an asynchronous operation in the future.
* Promise是一个类
*/
export final class Promise<out T> implements PromiseLike<T> {
private constructor() {
}
// 可以接收只有resolve回调的函数
constructor(callback: (resolve: (value: T|PromiseLike<T>) => void) => void throws) {
try {
callback((value: T|PromiseLike<T>): void => {
this.doResolve<T>(value);
});
} catch (error) {
this.rejectImpl(error);
}
}
// NOTE(audovichenko): add constructor #17147
// constructor(callback: (resolve: (value: T|PromiseLike<T>) => void throws,
// reject: () => void) => void throws)
constructor(callback: (resolve: (value: T|PromiseLike<T>) => void throws,
reject: (error: NullishType) => void) => void throws) {
try {
// 初始化Promise的时候,立即调用callback
callback((value: T|PromiseLike<T>): void => {
// 外部调用resolve的时候,内部调用doResolve
this.doResolve<T>(value);
}, (error: NullishType): void => {
// 外部调用reject的时候,内部调用rejectImpl
this.rejectImpl(error);
});
} catch (error) {
// 如果执行callback的过程中报错,Promise立马转为rejected状态
this.rejectImpl(error);
}
}
// NOTE(audovichenko): Change return type to Promise<U>. Issue #15523
// then的回调函数不包含参数
then<U>(onFulfilled: () => U|PromiseLike<U> throws): PromiseLike<U> {
// 调用then方法的时候,内部会生成一个Promise并返回
let promise = new Promise<U>();
// 将then的参数封装成fn
let fn: () => void = (): void => {
try {
if (this.state == Promise.STATE_RESOLVED) {
promise.doResolve<U>(onFulfilled());
} else {
promise.rejectImpl(this.value);
}
} catch (error) {
promise.rejectImpl(error);
}
}
// 如果此时promise的状态不是pendding,说明状态已经变成了最终值fulfilled或者rejected。
// 直接添加到微任务队列,微任务队列的任务会一直执行直到清空
if (this.state != Promise.STATE_PENDING) {
Promise.addToJobQueue(fn as Object);
} else {
// 如果此时promise的状态还是pendding,将fn添加到then状态队列
this.pushToThenQueue(fn as Object);
}
return promise;
}
// then的回调函数包含一个参数
// NOTE(audovichenko): Change return type to Promise<U>. Issue #15523
then<U>(onFulfilled: (value: T) => U|PromiseLike<U> throws): PromiseLike<U> {
let promise = new Promise<U>();
let fn: () => void = (): void => {
try {
if (this.state == Promise.STATE_RESOLVED) {
// 回调的时候将this.value回传
promise.doResolve<U>(onFulfilled(this.value!));
} else {
promise.rejectImpl(this.value! as Object);
}
} catch (e) {
promise.rejectImpl(e);
}
}
if (this.state != Promise.STATE_PENDING) {
Promise.addToJobQueue(fn as Object);
} else {
this.pushToThenQueue(fn as Object);
}
return promise;
}
// NOTE(audovichenko): Change return type to Promise<U>. Issue #15523
// NOTE(audovichenko): Mark parameters by '?' instead of add '|undefined'. Issue #17204
// 传入两个参数的then方法
then<U, E = never>(onFulfilled: ((value: T) => U|PromiseLike<U> throws)|undefined,
onRejected: ((error: NullishType) => E|PromiseLike<E> throws)|undefined): PromiseLike<U|E> {
let promise = new Promise<U|E>();
// 封装一个箭头函数作为微任务的一个任务
let fn: () => void = (): void => {
try {
// 如果是resolved状态
if (this.state == Promise.STATE_RESOLVED) {
// 如果then方法里传了onFulfilled回调函数,则把onFulfilled的结果值作为参数后调用doResolve
if (onFulfilled) {
promise.doResolve<U>(onFulfilled(this.value!));
} else {
promise.resolveImpl(this.value!);
}
} else {
// 如果then方法里传了onRejected回调函数,则把onRejected的结果值作为参数后调用rejectImpl
if (onRejected) {
promise.rejectImpl(onRejected(this.value! as Object));
} else {
promise.rejectImpl(this.value!);
}
}
} catch (e) {
promise.rejectImpl(e);
}
}
// 如果已经变了状态,添加到微任务队列。在队列中任务执行的时候会在fn中判断状态
if (this.state != Promise.STATE_PENDING) {
Promise.addToJobQueue(fn as Object);
} else {
// pending状态下,直接将函数入队即可。因为此时promise还没变状态
this.pushToThenQueue(fn as Object);
this.pushToCatchQueue(fn as Object);
}
return promise;
}
// catch方法
catch<U = never>(onRejected?: (error: NullishType) => U|PromiseLike<U> throws): Promise<T|U> {
// 声明一个新的promise 并最终将这个promise返回
let promise = new Promise<T|U>();
// 封装一个任务
let fn: () => void = (): void => {
try {
// 如果传入了onRejected,将onRejected的返回值作为参数调用doResolve,将本状态修改为resolved
// 所以即使onRejected是一个错误回调。Promise也仍然将它视作一个正常值
if (onRejected) {
let res: U|PromiseLike<U> = onRejected(this.value);
promise.doResolve<U>(res);
} else {
promise.rejectImpl(this.value!);
}
} catch (e) {
promise.rejectImpl(e);
}
}
// 判断是否已经是rejected,如果是则将任务添加到微任务队列
if (this.state == Promise.STATE_REJECTED) {
Promise.addToJobQueue(fn as Object);
} else if (this.state == Promise.STATE_PENDING) {
// 如果还处于pending状态,把封装的任务放到catchQueue中
this.pushToCatchQueue(fn as Object);
}
return promise;
}
// finally和then、catch方法一样,都会返回一个新的Promise
finally(onFinally?: () => void throws): Promise<T> {
let promise = new Promise<T>();
let fn: () => void = (): void => {
try {// 如果onFinally传入了参数,就执行传入的参数
if (onFinally) {
try {
onFinally();
} catch(error) {
// 如果执行出错,则新的promise状态变为rejected
promise.rejectImpl(error);
}
}
// 根据当前Promise的不同状态执行 新promise的resolve或reject
if (this.state == Promise.STATE_RESOLVED) {
promise.resolveImpl(this.value!);
} else {
promise.rejectImpl(this.value! as Object);
}
} catch (e) {
promise.rejectImpl(e);
}
}
// 和then、catch一样。pending状态推入各自的回调队列。非pending状态将任务推入微任务队列
if (this.state != Promise.STATE_PENDING) {
Promise.addToJobQueue(fn as Object);
} else {
this.pushToThenQueue(fn as Object);
this.pushToCatchQueue(fn as Object);
}
return promise;
}
// 返回一个resolved状态的promise
static resolve(): Promise<void> {
let p = new Promise<void>();
p.resolveImpl(undefined);
return p;
}
static resolve<U>(value: U|PromiseLike<U>): Promise<U> {
let p = new Promise<U>();
p.doResolve(value);
return p;
}
static reject(): Promise<void> {
let p = new Promise<void>();
p.rejectImpl(null);
return p;
}
// 返回一个rejected状态的promise
static reject<U = never>(value: U): Promise<U> {
let p = new Promise<U>();
p.rejectImpl(value);
return p;
}
static all<U>(promises: (U|PromiseLike<U>)[]): Promise<U[]> {
return new Promise<U[]>((resolve: (value: U[]) => void, reject: (error: NullishType) => void): void => {
// This temp object is needed because es2panda cannot change captured primitives
new AllPromiseConcurrency<U>(resolve, reject).all(promises);
});
}
static all<U>(promises: Iterable<U|PromiseLike<U>>): Promise<U[]> {
return Promise.all<U>(Promise.toArray(promises));
}
static allSettled<U>(promises: (U|PromiseLike<U>)[]): Promise<PromiseSettledResult<U>[]> {
return new Promise<PromiseSettledResult<U>[]>((resolve: (value: PromiseSettledResult<U>[]) => void, reject: (error: NullishType) => void): void => {
// This temp object is needed because es2panda cannot change captured primitives
new AllSettledPromiseConcurrency<U>(resolve, reject).allSettled(promises);
});
}
static allSettled<U>(promises: Iterable<U|PromiseLike<U>>): Promise<PromiseSettledResult<U>[]> {
return Promise.allSettled<U>(Promise.toArray(promises));
}
static any<U>(promises: (U|PromiseLike<U>)[]): Promise<U> {
return new Promise<U>((resolve: (value: U) => void, reject: (error: NullishType) => void): void => {
// This temp object is needed because es2panda cannot change captured primitives
new AnyPromiseConcurrency<U>(resolve, reject).any(promises);
});
}
static any<U>(promises: Iterable<U|PromiseLike<U>>): Promise<U> {
return Promise.any<U>(Promise.toArray(promises));
}
// 每一个同名重载方法,都接受迭代器类型和数组类型
static race<U>(promises: (U|PromiseLike<U>)[]): Promise<U> {
return new Promise<U>((resolve: (value: U) => void, reject: (error: NullishType) => void): void => {
// This temp object is needed because es2panda cannot change captured primitives
new RacePromiseConcurrency<U>(resolve, reject).race(promises);
});
}
static race<U>(promises: Iterable<U|PromiseLike<U>>): Promise<U> {
return Promise.race<U>(Promise.toArray(promises));
}
// 将迭代器转换成数组
private static toArray<U>(values: Iterable<U>): U[] {
let it = values.iterator();
let v = it.next();
// 如果迭代器一个元素都没有,返回一个空数组
if (v.done) {
return new U[0];
}
let array: U[] = new U[1];
array[0] = v.value!;
let len = 1;
while (true) {
v = it.next();
if (v.done) {
return Promise.trim(array, len);
}
array = Promise.ensureCapacity(array, len + 1);
array[len] = v.value!;
++len;
}
}
// 数组缩容操作。如果满了就不缩容。否则重新生成一个数组,并将原数组拷贝过去
private static trim<U>(array: U[], length: int): U[] {
if (array.length == length) {
return array;
}
let result = new U[length];
for (let i = 0; i < length; ++i) {
result[i] = array[i];
}
return result;
}
// 确保数组至少有size的容量
private static ensureCapacity<U>(queue: U[], size: int): U[] {
// 说明size所在的下标还可以存储数据
if (size <= queue.length) {
return queue;
}
// 进行两倍的扩容
let newQueue = new U[size * 2];
for (let i = 0; i < queue.length; ++i) {
newQueue[i] = queue[i];
}
return newQueue;
}
// 推入到then队列
private pushToThenQueue(job: Object): void {
// 保证this.catchQueue一定存在
// length property of thenQueue Objet array represents its capacity, thenQueueSize represents its current size
if (this.thenQueue == null) {
this.thenQueue = Promise.EMPTY_QUEUE;
}
// 保证数组可以存放对应位置的job。ensureCapacity会做一些扩容
this.thenQueue = this.ensureCapacity(this.thenQueue!, this.thenQueueSize + 1);
this.thenQueue![this.thenQueueSize] = job;
++this.thenQueueSize;
}
// 推入到catch队列
private pushToCatchQueue(job: Object): void {
// 保证this.catchQueue一定存在
// length property of catchQueue Objet array represents its capacity, catchQueueSize represents its current size
if (this.catchQueue == null) {
this.catchQueue = Promise.EMPTY_QUEUE;
}
// 保证数组可以存放对应位置的job。ensureCapacity会做一些扩容
this.catchQueue = this.ensureCapacity(this.catchQueue!, this.catchQueueSize + 1);
this.catchQueue![this.catchQueueSize] = job;
++this.catchQueueSize;
}
// 如果参数是一个Promise。本Promise的状态和参数Promise的状态保持一致
private subscribeOnAnotherPromise<U>(internalPromise: PromiseLike<U>): void {
let thisPromise = this;
// 本Promise的状态和参数Promise的状态保持一致
internalPromise.then<void, void>((value: U): void => {
thisPromise.resolveImpl(value);
}, (error: NullishType): void => {
thisPromise.rejectImpl(error);
});
}
// 执行resolve动作
private doResolve<U>(value: U|PromiseLike<U>): void {
if (value instanceof PromiseLike) {
this.subscribeOnAnotherPromise<U>(value as PromiseLike<U>);
} else {
this.resolveImpl(value);
}
}
// 一些native实现的方法
public native awaitResolution(): T;
private native resolveImpl<U>(value: U): void;
private native rejectImpl(error: NullishType): void;
private static native addToJobQueue(job: Object): void;
// pendding、fulfilled、rejected状态定义
private static STATE_PENDING = 0;
private static STATE_RESOLVED = 1;
private static STATE_REJECTED = 2;
private static EMPTY_QUEUE = new Object[0];
// Order of fields should be the following
// 1. Reference fields
// 2. Primitive fields in mostly size decreasing order
// filling alignment holes
// resolve时的返回值
private value: T | null = null;
// then方法传入的回调函数的数组
private thenQueue: Object[]|null = null;
// catch方法传入的回调函数的数组
private catchQueue: Object[]|null = null;
private linkedPromise: Object | null = null;
private eventPtr: long;
private thenQueueSize: int = 0;
private catchQueueSize: int = 0;
// Promise内的状态
private state: int = Promise.STATE_PENDING;
}
class PromiseConcurrencyBase<T> {
// 如果是PromiseLike直接返回,否则将值包装到Promise.resolve中
toPromiseLike(value: T|PromiseLike<T>): PromiseLike<T> {
if (value instanceof PromiseLike) {
let p = value as PromiseLike<T>;
return p;
} else {
let p = Promise.resolve<T>(value as T);
return p;
}
}
}
class AllPromiseConcurrency<T> extends PromiseConcurrencyBase<T> {
constructor(resolve: (value: T[]) => void, reject: (error: NullishType) => void) {
this.resolve = resolve;
this.reject = reject;
}
// Promiase.all的实现
all(promises: (T|PromiseLike<T>)[]): void {
this.values = new T[promises.length];
if (promises.length == 0) {
this.resolve(this.values!);
return;
}
for (let i = 0; i < promises.length; ++i) {
let idx = i;
this.toPromiseLike(promises[idx]).then<void, void>((value: T): void => {
this.resolveImpl(value, idx);
}, (error: NullishType): void => {
// 只要有一个失败,就失败
this.reject(error);
});
}
}
// 成功时使用resolvedCnt统计数量,总数量和promises数量相等时回调
private resolveImpl(value: T, idx: int): void {
this.values![idx] = value;
++this.resolvedCnt;
if (this.resolvedCnt == this.values!.length) {
this.resolve(this.values!);
}
}
private resolvedCnt = 0;
private rejectedCnt = 0;
private values: T[]|null = null;
private resolve: (value: T[]) => void;
private reject: (error: NullishType) => void;
}
class AllSettledPromiseConcurrency<T> extends PromiseConcurrencyBase<T> {
constructor(resolve: (value: PromiseSettledResult<T>[]) => void, reject: (error: NullishType) => void) {
this.resolve = resolve;
this.reject = reject;
}
// Promiase.allSettled的实现
allSettled(promises: (T|PromiseLike<T>)[]): void {
this.values = new PromiseSettledResult<T>[promises.length];
if (promises.length == 0) {
this.resolve(this.values!);
return;
}
for (let i = 0; i < promises.length; ++i) {
let idx = i;
this.toPromiseLike(promises[idx]).then<void, void>((value: T): void => {
this.resolveImpl(value as T, idx);
}, (error: NullishType): void => {
this.rejectImpl(error, idx);
});
}
}
// 成功和失败,都使用resolvedCnt统计数量,总数量和promises数量相等时回调
private resolveImpl(value: T, idx: int): void {
let res = new PromiseFulfilledResult<T>();
res.status = "fulfilled";
res.value = value;
this.values![idx] = res;
++this.resolvedCnt;
if (this.resolvedCnt == this.values!.length) {
this.resolve(this.values!);
}
}
// 成功和失败,都使用resolvedCnt统计数量,总数量和promises数量相等时回调
private rejectImpl(error: NullishType, idx: int): void {
let res = new PromiseRejectedResult();
res.status = "rejected";
res.reason = error;
this.values![idx] = res;
++this.resolvedCnt;
if (this.resolvedCnt == this.values!.length) {
this.resolve(this.values!);
}
}
private resolvedCnt = 0;
private rejectedCnt = 0;
private values: PromiseSettledResult<T>[]|null = null;
private resolve: (value: PromiseSettledResult<T>[]) => void;
private reject: (error: NullishType) => void;
}
class AnyPromiseConcurrency<T> extends PromiseConcurrencyBase<T> {
constructor(resolve: (value: T) => void, reject: (error: NullishType) => void) {
this.resolve = resolve;
this.reject = reject;
}
// Promiase.any的实现
any(promises: (T|PromiseLike<T>)[]): void {
this.errors = new Error[promises.length];
if (promises.length == 0) {
this.reject(new AggregateError(this.errors!, "All promises are rejected"));
return;
}
for (let i = 0; i < promises.length; ++i) {
let idx = i;
this.toPromiseLike(promises[idx]).then<void, void>((value: T): void => {
this.resolveImpl(value as T);
}, (error: NullishType): void => {
this.rejectImpl(error, idx);
});
}
}
// 只要一个成功,就算成功
private resolveImpl(value: T) {
++this.resolvedCnt;
if (this.resolvedCnt == 1) {
this.resolve(value);
}
}
// 统计失败的数量,全部失败,才算失败
private rejectImpl(error: NullishType, idx: int) {
++this.rejectedCnt;
if (error == null || error instanceof Error) {
this.errors![idx] = error as Error;
} else {
this.errors![idx] = new Error(error!.toString());
}
if (this.rejectedCnt == this.errors!.length) {
this.reject(new AggregateError(this.errors!, "All promises are rejected"));
}
}
private resolvedCnt = 0;
private rejectedCnt = 0;
private errors: Error[]|null = null;
private resolve: (value: T) => void;
private reject: (error: NullishType) => void;
}
class RacePromiseConcurrency<T> extends PromiseConcurrencyBase<T> {
constructor(resolve: (value: T) => void, reject: (error: NullishType) => void) {
this.resolve = resolve;
this.reject = reject;
}
// Promise.race方法的实现
race(promises: (T|PromiseLike<T>)[]): void {
// 遍历传入的promise,只要有一个状态发生变化。this的状态就发生变化,同时settled设置为true
for (let i = 0; i < promises.length; ++i) {
let idx = i;
this.toPromiseLike(promises[idx]).then<void, void>((value: T): void => {
this.resolveImpl(value as T);
}, (error: NullishType): void => {
this.rejectImpl(error);
});
}
}
// 只要成功,就将settled置为true
private resolveImpl(value: T) {
if (!this.settled) {
this.resolve(value)
this.settled = true;
}
}
// 只要失败,就将settled置为true
private rejectImpl(error: NullishType) {
if (!this.settled) {
this.reject(error);
this.settled = true;
}
}
private settled = false;
private resolve: (value: T) => void;
private reject: (error: NullishType) => void;
}
export type NullablePromise<out T> = Promise<T> | null;
resolveImpl、rejectImpl、addToJobQueue
我们从上面最常规的Promise类里面没有发现这几个函数的实现。全局搜索一下发现他们有对应的原生实现
resolveImpl
void EtsPromiseResolve(EtsPromise *promise, EtsObject *value)
这个方法里面也判断了value,如果value是一个Promise,本Promise的结果是依赖于value这个Promise的
最终会调用hpromise→Resolve(coro, value)修改Promise的状态,之后遍历执行thenQueue中存放的回调
void Resolve(EtsCoroutine *coro, EtsObject *value)
这里主要是修改了Promise的状态为Resolved,同时把成功的值放入到promise中
static void OnPromiseCompletion(EtsCoroutine *coro, EtsHandle &promise, EtsHandle &queue, EtsInt length)
OnPromiseCompletion中完成对thenQueue队列的处理,遍历队列里的每一个回调,然后执行
rejectImpl
void EtsPromiseReject(EtsPromise *promise, EtsObject *error)
调用hpromise->Reject(coro, error) 修改Promise的状态为rejected,然后遍历catchQueue进行回调函数的调用
然后调用OnPromiseCompletion完成对catchQueue的遍历回调。
可以看到不管是resolve还是reject,最终都是调用了OnPromiseCompletion。为什么resolve和reject都能调用OnPromiseCompletion,是因为入队的元素都是被包装过的箭头函数
void Reject(EtsCoroutine *coro, EtsObject *error)
主要是修改了状态为Rejected,同时把值塞入到error中
addToJobQueue
可以看到最终只是把回调添加到了jobQueue里面。这个jobQueue就是用于存放微任务的队列
void EtsPromiseAddToJobQueue(EtsObject *callback)
添加到微任务队列,主要是调用了jobQueue的AddJob方法
void JsJobQueue::AddJob(EtsObject *callback)
可以看到添加到微任务队列的任务,在最底层又被C API包装成了一个promise,原有的任务会在这个promise对象的then方法回调中执行
static napi_value ThenCallback(napi_env env, napi_callback_info info)
ThenCallback这个函数实际上就是调用了上一步封装好的callbackRef
Ets中的setTimeout、setInternal
Ets中setTimeout和setInternal的实现位于 gitee.com/openharmony… 中
void JsiTimerModule::InitTimerModule(const shared_ptr& runtime, shared_ptr& moduleObj)
setTimeout和setInternal都是使用 SetTimeoutOrInterval 实现的
shared_ptr SetTimeoutOrInterval(const shared_ptr& runtime, const shared_ptr& thisObj, const std::vector<shared_ptr>& argv, int32_t argc, bool isInterval)
uint32_t JsiTimerModule::AddCallBack(const shared_ptr& func, const std::vector<shared_ptr>& params)
按照callbackId存储到两个对应的Map中
void ClearTimeoutOrInterval(const shared_ptr& runtime, const shared_ptr& thisObj, const std::vector<shared_ptr>& argv, int32_t argc)
clear方法根据传入的callbackId,将回调以及参数从对应的Map中移除
分析代码执行
宏任务、微任务
async function async1() {
console.log('t111 async1 start');
await async2();
console.log('t111 async1 end');
}
async function async2() {
console.log('t111 async2');
}
console.log('t111 script start');
setTimeout(()=> {
console.log('t111 setTimeout');
}, 0)
async1();
new Promise<void>((resolve) =>{
console.log('t111 promise1');
resolve();
}).then(() =>{
console.log('t111 promise2');
});
console.log('t111 script end');
/**
06-06 14:02:26.533 22305-22305 A03D00/JSAPP pid-22305 I t111 script start
06-06 14:02:26.533 22305-22305 A03D00/JSAPP pid-22305 I t111 async1 start
06-06 14:02:26.533 22305-22305 A03D00/JSAPP pid-22305 I t111 async2
06-06 14:02:26.533 22305-22305 A03D00/JSAPP pid-22305 I t111 promise1
06-06 14:02:26.533 22305-22305 A03D00/JSAPP pid-22305 I t111 script end
06-06 14:02:26.534 22305-22305 A03D00/JSAPP pid-22305 I t111 async1 end
06-06 14:02:26.534 22305-22305 A03D00/JSAPP pid-22305 I t111 promise2
06-06 14:02:26.542 22305-22305 A03D00/JSAPP pid-22305 I t111 setTimeout
*/
先执行宏任务(当前代码块也算是宏任务),然后执行当前宏任务产生的微任务,然后接着执行宏任务
- 从上往下执行代码,先执行同步代码,输出 t111
script start - 遇到setTimeout,把 setTimeout 的代码放到宏任务队列中
- 执行 async1(),输出 t111
async1 start, 然后执行 async2(), 输出 t111async2,把 async2() 后面的代码console.log('t111 async1 end')放到微任务队列中 - 接着往下执行,输出
promise1,把 .then()放到微任务队列中;注意Promise本身是同步的立即执行函数,.then是异步执行函数 - 接着往下执行, 输出 t111
script end。同步代码(同时也是宏任务)执行完成,接下来开始执行刚才放到微任务中的代码 - 依次执行微任务中的代码,依次输出 t111
async1 end、 t111promise2, 微任务中的代码执行完成后,开始执行宏任务中的代码,输出 t111setTimeout
宏任务产生微任务
console.log('t222 start');
setTimeout(() => {
console.log('t222 children2');
Promise.resolve().then(() => {
console.log('t222 children3');
})
}, 0);
new Promise<string>((resolve, reject) =>{
console.log('t222 children4');
setTimeout(() =>{
console.log('t222 children5');
resolve('t222 children6')
}, 0)
}).then((res) => {
console.log('t222 children7');
setTimeout(() => {
console.log(res);
}, 0)
})
/**
06-06 14:05:16.276 23368-23368 A03D00/JSAPP pid-23368 I t222 start
06-06 14:05:16.277 23368-23368 A03D00/JSAPP pid-23368 I t222 children4
06-06 14:05:16.284 23368-23368 A03D00/JSAPP pid-23368 I t222 children2
06-06 14:05:16.284 23368-23368 A03D00/JSAPP pid-23368 I t222 children3
06-06 14:05:16.284 23368-23368 A03D00/JSAPP pid-23368 I t222 children5
06-06 14:05:16.284 23368-23368 A03D00/JSAPP pid-23368 I t222 children7
06-06 14:05:16.284 23368-23368 A03D00/JSAPP pid-23368 I t222 children6
*/
- 从上往下执行代码,先执行同步代码,输出 t222
start - 遇到setTimeout,把 setTimeout 的代码放到宏任务队列①中
- 接着往下执行,输出 t222
children4, 遇到setTimeout,把 setTimeout 的代码放到宏任务队列②中,此时.then并不会被放到微任务队列中,因为 resolve是放到 setTimeout中执行的 - 代码执行完成之后,会查找微任务队列中的事件,发现并没有,于是开始执行宏任务①,即第一个 setTimeout, 输出 t222
children2,此时,会把Promise.resolve().then放到微任务队列中。 - 宏任务①中的代码执行完成后,会查找微任务队列,于是输出 t222
children3;然后开始执行宏任务②,即第二个 setTimeout,输出 t222children5,此时将.then放到微任务队列中。 - 宏任务②中的代码执行完成后,会查找微任务队列,于是输出 t222
children7,遇到 setTimeout,放到宏任务队列中。此时微任务执行完成,开始执行宏任务,输出 t222children6;
微任务产生宏任务
// 1. js全局上下文入栈,遇到new Promise()同步任务执行打印promise1 resolve()后 .then异步函数被推入微任务队列
new Promise<void>((resolve, reject) => {
console.log('t333 promise1')
resolve()
}).then(() => {
// 8. 执行完当前主线程后依次执行当前微任务队列、直至清空、队列的结构是先进先出、所以依次如下
// 执行第一个微任务打印promise2
console.log('t333 promise2')
})
// 2. 遇到console.log('start1')同步立即执行打印start1
console.log('t333 start1')
// 3. 遇到setTimeout 异步任务2s后把要执行的任务推入宏任务队列中
setTimeout(() => {
// 16. 打印a
console.log('t333 a')
},2000)
// 4. 遇到setTimeout 异步任务1s后把要执行的任务推入宏任务队列中
setTimeout(() => {
// 15. 打印b
console.log('t333 b')
},1000)
// 5. 遇到setTimeout 异步任务立即把要执行的任务推入宏任务队列中
setTimeout(() => {
// 11. 遇到promsie.resolve()执行将then方法推入微任务队列
Promise.resolve().then(() => {
// 清空当前微任务队列打印promiseA 将后面的then又推入微任务队列
console.log('t333 promiseA')
}).then(() => {
// 12. 清空当前微任务队列打印promiseB
// 判断当前微任务队列是否为空、为空将下一个宏任务入栈并执行
console.log('t333 promiseB')
})
})
// 6. 遇到Promise执行resolve后将第一个then方法推入微任务队列中
Promise.resolve().then(() => {
// 9. 执行第二个微任务打印promise88
console.log('promise88')
// 紧接着遇到setTimeout 异步任务立即把要执行的任务推入宏任务队列中、将后面的then推入微任务队列中
setTimeout(() => {
// 13. 打印333
console.log("t333 333")
})
}).then(() => {
// 10. 执行第三个微任务打印promise2。
console.log('promise2')
// 紧接着遇到setTimeout 异步任务立即把要执行的任务推入宏任务队列中
// 检查当前微任务队列是否为空、为空将宏任务队列中的下一个入栈执行
setTimeout(() => {
// 14. 打印555
console.log("t333 555")
})
})
// 7. 遇到console.log('start2')执行打印start2。此时打印了 promise1、start1、start2
console.log('t333 start2')
/**
06-12 22:12:12.849 15723-15723 A03D00/JSAPP pid-15723 I t333 promise1
06-12 22:12:12.849 15723-15723 A03D00/JSAPP pid-15723 I t333 start1
06-12 22:12:12.849 15723-15723 A03D00/JSAPP pid-15723 I t333 start2
06-12 22:12:12.849 15723-15723 A03D00/JSAPP pid-15723 I t333 promise2
06-12 22:12:12.849 15723-15723 A03D00/JSAPP pid-15723 I t333 promise88
06-12 22:12:12.856 15723-15723 A03D00/JSAPP pid-15723 I t333 promiseA
06-12 22:12:12.856 15723-15723 A03D00/JSAPP pid-15723 I t333 promiseB
06-12 22:12:12.856 15723-15723 A03D00/JSAPP pid-15723 I t333 333
06-12 22:12:12.856 15723-15723 A03D00/JSAPP pid-15723 I t333 555
06-12 22:12:13.851 15723-15723 A03D00/JSAPP com.unrav...lication I t333 b
06-12 22:12:14.849 15723-15723 A03D00/JSAPP com.unrav...lication I t333 a
*/
微任务产生微任务
// 1. Promise.sesolve创建了一个fulfilled状态的promise,然后调用then方法,将then的参数放入到微任务队列
Promise.resolve().then(() => {
console.log('t444 0');
// 如果then里面返回了一个promise,那么then本身返回的promise的结果依赖then方法返回的promise的结果。
// 相当于间接增加了一个微任务Job
return Promise.resolve('t444 4');
}).then((res) => {
// 如果then方法接受一个参数,并且这个参数是一个Promise的时候
console.log(res)
})
// 2. Promise.sesolve创建了一个fulfilled状态的promise,然后调用then方法,将then的参数放入到微任务队列
Promise.resolve().then(() => {
console.log('t444 1');
}).then(() => {
console.log('t444 2');
}).then(() => {
console.log('t444 3');
}).then(() => {
console.log('t444 5');
}).then(() =>{
console.log('t444 6');
})
/**
06-07 13:13:15.029 20470-20470 A03D00/JSAPP com.unrav...lication I t444 0
06-07 13:13:15.030 20470-20470 A03D00/JSAPP com.unrav...lication I t444 1
06-07 13:13:15.030 20470-20470 A03D00/JSAPP com.unrav...lication I t444 2
06-07 13:13:15.030 20470-20470 A03D00/JSAPP com.unrav...lication I t444 3
06-07 13:13:15.030 20470-20470 A03D00/JSAPP com.unrav...lication I t444 4
06-07 13:13:15.030 20470-20470 A03D00/JSAPP com.unrav...lication I t444 5
06-07 13:13:15.030 20470-20470 A03D00/JSAPP com.unrav...lication I t444 6
*/
参考资料
- MDN - Promise developer.mozilla.org/zh-CN/docs/…
- Promise对象 https://wangdoc.com/es6/promise
- Generator函数的语法 wangdoc.com/es6/generat…
- Generator函数的异步应用 wangdoc.com/es6/generat…
- Symbol https://wangdoc.com/es6/symbol
- async 函数 wangdoc.com/es6/async
- 异步遍历器 wangdoc.com/es6/async-i…
- PromiseA+规范[ promisesaplus.com/]promisesaplus.com/)
- PromiseA+规范中文翻译版 www.ituring.com.cn/article/665…
- Dart中的生成器 https://dart.cn/language/functions#generators
- Ark编译器核心 https://gitee.com/openharmony/arkcompiler_runtime_core
- Ark引擎 https://gitee.com/openharmony/arkui_ace_engine
- 八段代码彻底掌握 Promise juejin.cn/post/684490…
- 从一道让我失眠的 Promise 面试题开始,深入分析 Promise 实现细节 juejin.cn/post/694531… 15.【建议星星】要就来45道Promise面试题一次爽到底(1.1w字用心整理) juejin.cn/post/684490…
- 9k字 | Promise/async/Generator实现原理解析 juejin.cn/post/684490…
- 2K字就能理解的async/await原理,还要拖多久?segmentfault.com/a/119000004…
- 面试官:“你能手写一个 Promise 吗” https://zhuanlan.zhihu.com/p/183801144
- 图解 Promise 实现原理(一)—— 基础实现 zhuanlan.zhihu.com/p/58428287