promise

  1. 描述 promise 框架 - 规范

    • promise 有哪些状态?对应值有哪些 - pending、fulfilled、rejected
    • new Promise 执行器 executor(),执行器参数是? - resolve、reject
    • promise 的默认状态是?promise 状态的流转? - 默认 pending,pf,pr
    • promise,value 保存成功状态的枚举?- undefined/thenable/promise
    • promise,失败状态值?- reason 保存失败
  2. 描述 promise 接口

    • promise 一定会有 then,then 接收来源,两个回调 onFulfilled(value) + onRejected(reason)
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
const PENDING = "pending";
const FULFILLED = "fulfilled";
const REJECTED = "rejected";

class MPromise {
#state = PENDING;
#result = undefined;
#handlers = [];

constructor(executor) {
const resolve = (data) => {
this.#changeState(FULFILLED, data);
};
const reject = (reason) => {
this.#changeState(REJECTED, reason);
};
try {
executor(resolve, reject);
} catch (err) {
reject(err);
}
}

#changeState(state, result) {
if (this.#state !== PENDING) return;
this.#state = state;
this.#result = result;
this.#run();
}

#isPromiseLike(value) {}

#runMicroTask(func) {
if (typeof process === "object" && typeof process.nextTick === "function") {
process.nextTick(func);
} else if (typeof MutationObserver === "function") {
const ob = new MutationObserver(func);
const textNode = document.createTextNode("1");
ob.observe(textNode, {
characterData: true,
});
textNode.data = "2";
} else {
setTimeout(func, 0);
}
}

#runOne(callback, resolve, reject) {
this.#runMicroTask(() => {
if (typeof callback !== "function") {
const settled = this.#state === FULFILLED ? resolve : reject;
settled(this.#result);
return;
}
try {
const data = callback(this.#result);
if (this.#isPromiseLike(data)) {
data.then(resolve, reject);
} else {
resolve(data);
}
} catch (err) {
reject(err);
}
});
}

#run() {
if (this.#state !== PENDING) return;
while (this.#handlers.length) {
const { onFulfilled, onRejected, resolve, reject } =
this.#handlers.shift();

if (this.#state === FULFILLED) {
this.#runOne(onFulfilled, resolve, reject);
} else if (this.#state === REJECTED) {
this.#runOne(onRejected, resolve, reject);
} else {
// 异步
}
}
}

then(onFulfilled, onRejected) {
return new MyPromise((resolve, reject) => {
this.#handlers.push({
onFulfilled,
onRejected,
resolve,
reject,
});
this.#run();
});
}

all(proms) {
let res, ref;
const p = new MyPromise((resolve, reject) => {
res = resolve;
rej = reject;
});
// 设置 p 的状态
const result = [];
let count = 0; // 数量
let fulFilledCount = 0; // 完成的数量
for (const prom of proms) {
const i = count;
count++;
MyPromise.resolve(prom).then((data) => {
// 将成功数据汇总result
result[i] = data;
// 全部完成
fulFilledCount++;
if (fulFilledCount === count) {
res(result);
}
}, rej);
}
if (count === 0) {
res(result);
}
return p;
}

// 以下方法为 es6 添加的方法
catch(onRejected) {
return this.then(undefined, onRejected);
}

finally(onFinally) {
return this.then(
(data) => {
onFinally();
return data;
},
(err) => {
onFinally();
throw err;
}
);
}

static resolve(value) {
if (value instanceof MyPromise) return value;
let res, rej;
const p = new MyPromise((resolve, reject) => {
res = resolve;
rej = reject;
});
if (p.#isPromiseLike(value)) {
value.then(res, rej);
} else {
res(value);
}
return p;
}

static reject(reason) {
return new MyPromise((resolve, reject) => {
reject(reason);
});
}
}

A+ 规范

判断一个值是否是 Promise Like

1
2
3
4
5
6
7
function isPromiseLike(value) {
return (
value !== null &&
(typeof value === "object" || typeof value === "function") &&
typeof value.then === "function"
);
}

问题引入

为什么 promise resolve 了一个 value, 最后输出的 value 值确是 undefined

1
2
3
4
5
6
7
8
9
10
11
const test = new MPromise((resolve, reject) => {
setTimeout(() => {
resolve(111);
}, 1000);
}).then((value) => {
console.log("then");
});

setTimeout(() => {
console.log(test);
}, 3000);

因为现在这种写法, 相当于在.then 里 return undefined, 所以最后的 value 是 undefined,如果显式 return 一个值, 就不是 undefined 了;比如 return value

.then 返回的是一个新 Promise, 那么原来 promise 实现的时候, 用数组来存回调函数有什么意义?

  1. 情况 1,链式调用的时候,此时每一个.then 返回的都是一个新 promise, 所以每次回调数组 FULFILLED_CALLBACK_LIST 都是空数组。针对这种情况, 确实用数组来存储回调没意义, 完全可以就用一个变量来存储。

    1
    2
    3
    4
    5
    6
    7
    const test = new Promise((resolve, reject) => {
    setTimeout(() => {
    resolve(111);
    }, 1000);
    })
    .then((value) => {})
    .then(() => {});
  2. 情况 2,promise 实例是同一个, 数组的存在就有了意义

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    const test = new Promise((resolve, reject) => {
    setTimeout(() => {
    resolve(111);
    }, 1000);
    });

    test.then(() => {});
    test.then(() => {});
    test.then(() => {});
    test.then(() => {});

为什么在 catch 的回调里, 打印 promise, 显示状态是 pending

1
2
3
4
5
6
7
8
9
10
11
12
const test = new Promise((resolve, reject) => {
setTimeout(() => {
reject(111);
}, 1000);
}).catch((reason) => {
console.log("报错" + reason);
console.log(test); // pending
});

setTimeout(() => {
console.log(test); // fulfilled
}, 3000);
  • catch 函数会返回一个新的 promise, 而 test 就是这个新 promise
  • catch 的回调里, 打印 promise 的时候, 整个回调还并没有执行完成(所以此时的状态是 pending), 只有当整个回调完成了, 才会更改状态
  • catch 的回调函数, 如果成功执行完成了, 会改变这个新 Promise 的状态为 fulfilled

常用方法

  1. race:返回一个新的 Promise 实例
    • const p = Promise.race([p1, p2, p3]);
    • 只要 p1、p2、p3 之中有一个实例率先改变状态,p 的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给 p 的回调函数。
  2. all:返回一个新的 Promise 实例,适用于需要确保所有 Promise 成功完成才能继续执行的情况。
    • const p = Promise.all([a、b、c]);
    • a、b、c 都是 Promise 实例,如果不是,就会先调用下面讲到的 Promise.resolve 方法,将参数转为 Promise 实例,再进一步处理
    • 参数可以不是数组,但必须具有 Iterator 接口,且返回的每个成员都是 Promise 实例
  3. allSettled:适用于需要获取所有 Promise 的最终状态(无论成功或失败)的情况,而不是仅仅关注成功的结果
    • 状态只可能变成 fulfilled
  4. finally:(es2018)不接受任何参数,无法知道前面的 Promise 状态到底是 fulfilled 还是 rejected。这表明,finally 方法里面的操作,应该是与状态无关的,不依赖于 Promise 的执行结果。
  5. any:只要参数实例有一个变成 fulfilled 状态,包装实例就会变成 fulfilled 状态;如果所有参数实例都变成 rejected 状态,包装实例就会变成 rejected 状态。

async await & generator

promise 的语法糖,await 后面的语句放在 .then 回调里面

  • 被 async 定义的函数会默认返回一个 Promise 对象 resolve 的值
  • 对 async 函数可以直接 then,返回值就是 then 方法传入的函数
  • async await 是在 generator 的基础上,promise 执行器内部处理了中间状态,返回最终状态
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
async function foo() {
console.log(0);
const n = await 1;
console.log(n);
}

foo();
console.log(2); // 输出 0 2 1

// 相当于
function foo() {
return Promise.resolve(1).then((n) => {
console.log(n);
});
}
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
33
34
35
36
37
38
39
40
41
42
43
44
45
// 1. async await
function wait500(input) {
return new Promise((resolve, reject) => {
console.log("wait500", input);
setTimeout(() => {
resolve(input + 500);
}, 500);
});
}

wait500.then((res) => {
wait500.then((res) => {
wait500.then((res) => {
wait500.then((res) => {});
});
});
});

async function asyncCall() {
const result = await wait500(0);
result = await wait500(0);
result = await wait500(0);
result = await wait500(0);

console.log("asyncCall", result);
}

asyncCall();

// 2. generator 步进代替then
function* generator() {
let index = 0;
while (true) yield index++;
}

let gen = generator();
console.log(gen.next().value);
console.log(gen.next().value);
console.log(gen.next().value);
console.log(gen.next().value);

// 重点 - 结合流水线做自动化处理
const GEN_LINE = [1, 2, 3, 4, 5, 6](GEN_LINE || []).forEach((it) => {
console.log(gen.next(it).value);
});
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
// 定义了一个promise,用来模拟异步请求,作用是传入参数++
function getNum(num) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(num + 1);
}, 1000);
});
}

//自动执行器,如果一个Generator函数没有执行完,则递归调用
function asyncFun(func) {
var gen = func();

function next(data) {
var result = gen.next(data);
if (result.done) return result.value;
result.value.then(function (data) {
next(data);
});
}

next();
}

// 所需要执行的Generator函数,内部的数据在执行完成一步的promise之后,再调用下一步
var func = function* () {
var f1 = yield getNum(1);
var f2 = yield getNum(f1);
console.log(f2);
};
asyncFun(func);

generator 传值

1
2
3
4
5
6
7
8
9
10
11
function* simpleGen() {
const a = yield 1;
console.log(a); // 通过 next() 传入的值
const b = yield 2;
return a + b;
}

const gen = simpleGen();
gen.next(); // { value: 1, done: false }
gen.next(10); // { value: 2, done: false },且 a = 10
gen.next(20); // { value: 30, done: true },且 b = 20
  • gen.next(): 暂停在 yield 1 处,等待下一次 next() 调用,返回值: { value: 1, done: false },此时变量 a 尚未被赋值
  • gen.next(10): 参数 10 被传递给 Generator,成为第一个 yield 表达式的结果,赋值操作完成:const a = 10,执行 console.log(a) 输出 10,此时变量 b 尚未被赋值

yield 表达式的双重角色

  • 向外部返回右侧表达式的值
  • 从外部接收下一次 next() 调用传入的值

参数传递时机

  • 第一次 next() 调用通常不带参数 (传入的参数会被忽略)
  • 后续 next() 调用的参数会成为上一个 yield 表达式的结果值

执行流程控制

  • 每次 next() 调用会使 Generator 执行到下一个 yield 或 return
  • yield 暂停执行,next() 恢复执行