Promise & async
# Promise
- 主要用于异步计算
- 可以将异步操作队列化,按照期望的顺序执行,返回符合预期的结果
- 可以在对象之间传递和操作promise,帮助我们处理队列
实现一个 Promise
let resolvePromise = (promise2,x,resolve,reject) => {
// 判断x的类型 来处理promise2是成功还是失败
// 所有的promise都遵循这个规范,不同的人写的promise可能会混用
// 尽可能考虑的周全 要考虑别人promise可能出错的情况
if(promise2 === x){
return reject(new TypeError('循环引用'))
}
// 判断x是不是一个promise ,这个x 可能不是自己的promise 所以为了安全 需要在进行校验,放置调一起用成功和失败
if(typeof x === 'function' || (typeof x === 'object' && x !== null)){
// 尝试取当前x的then方法, 这个then方法可能别人定义的时候 用的Object.defineProperty
let called;
try{
let then = x.then; // 如果取then方法出错 那就用错误拒绝promise2
if(typeof then === 'function'){ // 我就认为他是一个promise
then.call(x,y=>{ // 让当前的promise执行 ,不用多次取then方法了
// y 有可能还是一个promise , 继续调用resolvePromise方法,直到解析出一个常量为止,最终把常量传递下去
if(called) return; // 放置此方法多次被调用
called = true;
resolvePromise(promise2,y,resolve,reject);
},r=>{
if(called) return;
called = true;
reject(r); // 让当前的promise变成失败态即可
})
}else{
// x就是一个普通的对象 并没有then方法
resolve(x);
}
}catch(e){
if(called) return;
called = true;
reject(e);
}
}else{
// x肯定一个常量
resolve(x);
}
}
class Promise{
constructor(executor){
this.status = 'pending';
this.value;
this.reason;
this.resolveCallbacks = []; // 当then是pending 我希望吧成功的方法都放到数组中
this.rejectCallbacks = [];
let resolve = (value)=>{
// 如果是promise就调用这个promise的then方法
if(value instanceof Promise){
// 不停的解析 等待着解析出一个常量 传递给下面
return value.then(resolve,reject);
}
if(this.status == 'pending'){
this.status = 'fulfilled';
this.value = value;
this.resolveCallbacks.forEach(fn=>fn()); // 发布
}
}
let reject = (reason)=>{
if(this.status === 'pending'){
this.status = 'rejected';
this.reason = reason;
this.rejectCallbacks.forEach(fn=>fn())
}
}
try{
executor(resolve,reject);
}catch(e){
reject(e);
}
}
then(onfulfilled,onrejected){ // onfulfilled,onrejected 是两个可选参数
onfulfilled = typeof onfulfilled === 'function'?onfulfilled:val=>val;
onrejected = typeof onrejected === 'function'?onrejected:r=>{throw r}
let promise2;
promise2 = new Promise((resolve,reject)=>{
if(this.status === 'fulfilled'){
setTimeout(()=>{
try{
let x = onfulfilled(this.value);
// x是普通值还是promise 如果是普通值 直接调用promise2的resolve
// 如果是promise 那应该让x这个promise执行 x.then
resolvePromise(promise2,x,resolve,reject);
}catch(e){
reject(e);
}
},0);
}
if(this.status === 'rejected'){
setTimeout(()=>{
try{
let x = onrejected(this.reason);
resolvePromise(promise2,x,resolve,reject);
}catch(e){
reject(e);
}
},0)
}
if(this.status === 'pending'){
this.resolveCallbacks.push(()=>{
setTimeout(()=>{
try{
let x = onfulfilled(this.value);
resolvePromise(promise2,x,resolve,reject);
}catch(e){
reject(e);
}
},0)
});
this.rejectCallbacks.push(()=>{
setTimeout(()=>{
try{
let x = onrejected(this.reason);
resolvePromise(promise2,x,resolve,reject);
}catch(e){
reject(e);
}
},0)
})
}
});
return promise2;
}
catch(rejectFunc){// catch的实现
return this.then(null,rejectFunc);
}
}
// 暴露一个方法这个方法需要返回一个对象 对象上需要有 promise resolve reject 三个属性
// 减少嵌套
Promise.defer = Promise.deferred = function(){
let dfd = {}
dfd.promise = new Promise((resolve,reject)=>{
dfd.resolve = resolve;
dfd.reject = reject;
})
return dfd;
}
// 产生成功的promise
Promise.resolve = function(value){
return new Promise((resolve,reject)=>{
resolve(value);
})
}
// 产生一个失败的promise
Promise.reject = function(reason){
return new Promise((resolve,reject)=>{
reject(reason);
})
}
Promise.all = function(values){
return new Promise((resolve,reject)=>{
let results = []; // 结果数组
let i = 0;
let processData = (value,index)=>{
results[index] = value;
// 当成功的个数 和 当前的参数个数相等就把结果抛出去
if(++i === values.length){
resolve(results);
}
}
for(let i = 0 ; i< values.length;i++){
let current = values[i]; // 拿到数组中每一项
// 判断是不是一个promise
if((typeof current === 'object' && current !==null)|| typeof current == 'function'){
// 如果是promise
if(typeof current.then == 'function'){
// 就调用这个promise的then方法,把结果和索引对应上,如果任何一个失败了返回的proimise就是一个失败的promise
current.then(y=>{
processData(y,i);
},reject);
}else{
processData(current,i);
}
}else{
processData(current,i);
}
}
});
}
// race的原理
Promise.race = function(values){
return new Promise((resolve,reject)=>{
for(let i = 0 ; i< values.length;i++){
let current = values[i];
if((typeof current === 'object' && current !==null)|| typeof current == 'function'){
let then = current.then;
if(typeof then == 'function'){ // 比较哪个promise比较快,谁快用快
then.call(current,resolve,reject)
}else{
resolve(current);
}
}else{
resolve(current);
}
}
});
}
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
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
# Generator
- generator也是为了解决地狱回调问题的,和promise一样都是为了实现异步编程,本质还是各种回调;
- generator为es6中新定义的数据类型,这种数据类型和函数很像,每个函数只能返回一个结果,即只能return一次, 如果在某些函数中没有看到return,其实质在函数结尾是存在一个隐藏的return undefined 的,而generator不同,可以返回多次
function* gen(){
yield 1;
yield 2;
yield 3;
yield 4;
yield 5;
return "结束";
}
let g = gen();
let i = 0;
let timer = setInterval(() => {//每间隔500ms执行一次g.next(),执行7次,并在控制台打印
i++;
console.log(g.next()); //执行gen函数
if(i>7){
clearInterval(timer);
}
}, 500);
try{
g.throw(new Error('test1'))
}catch(e){
console.log(e)
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
上述例子 可以看出generator 遇到yleld就会暂停,只有当调用generator.next()
才会向下执行,
调用这个方法会返回{value: x, done: true/false}
,这个对象中value是yield的返回值,
done表示generator是否执行结束,只有当执行到return时,这个对象中的done才会变成true,说明执行结束
# async/await
原理就是利用 generator(生成器)分割代码片段。然后我们使用一个函数让其自迭代,每一个yield 用 promise 包裹起来。执行下一步的时机由 promise 来控制
而且相较于Promise,async的优越性就是把每次异步返回的结果从then中拿到最外层的方法中,不需要链式调用,只要用同步的写法就可以了。 更加直观而且,更适合处理并发调用的问题。但是async必须以一个Promise对象开始 ,所以async通常是和Promise结合使用的
function _asyncToGenerator(fn) {
return function() {
var self = this,
args = arguments;
// 将返回值promise化
return new Promise(function(resolve, reject) {
// 获取迭代器实例
var gen = fn.apply(self, args);
// 执行下一步
function _next(value) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, 'next', value);
}
// 抛出异常
function _throw(err) {
asyncGeneratorStep(gen, resolve, reject, _next, _throw, 'throw', err);
}
// 第一次触发
_next(undefined);
});
};
}
function asyncGeneratorStep(gen, resolve, reject, _next, _throw, key, arg) {
try {
var info = gen[key](arg);
var value = info.value;
} catch (error) {
reject(error);
return;
}
if (info.done) {
// 迭代器完成
resolve(value);
} else {
// -- 这行代码就是精髓 --
// 将所有值promise化
// 比如 yield 1
// const a = Promise.resolve(1) a 是一个 promise
// const b = Promise.resolve(a) b 是一个 promise
// 可以做到统一 promise 输出
// 当 promise 执行完之后再执行下一步
// 递归调用 next 函数,直到 done == true
Promise.resolve(value).then(_next, _throw);
}
}
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
await 每次遇到await都会中断此次进程,然后去执行外面的同步代码,然后再进来,根据上次保存的next值,继续执行
async function foo() {
await console.log(121212)
console.log(121212)
}
2
3
4
function _foo() {
_foo = _asyncToGenerator(
regeneratorRuntime.mark(function _callee() {
return regeneratorRuntime.wrap(function _callee$(_context) {
while (1) {
switch (_context.prev = _context.next) {
case 0:
_context.next = 2;
return console.log(121212);
case 2:
console.log(121212);
case "end":
return _context.stop();
}
}
}, _callee);
}));
return _foo.apply(this, arguments);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
测试
const asyncFunc = _asyncToGenerator(function*() {
const e = yield new Promise(resolve => {
setTimeout(() => {
resolve('e');
}, 1000);
});
const a = yield Promise.resolve('a');
const d = yield 'd';
const b = yield Promise.resolve('b');
const c = yield Promise.resolve('c');
return [a, b, c, d, e];
});
asyncFunc().then(res => {
console.log(res); // ['a', 'b', 'c', 'd', 'e']
});
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
总的来说,async和generator函数主要就是为了解决异步的并发调用使用的 ,直接将参数从then里取出来,相比promise的链式调用,传参更加方便,异步顺序更加清晰
# 捕获错误
try catch
(async () => {
try {
const data = await fn()
} catch(err) {
console.log('err is ->', err)
}
})()
2
3
4
5
6
7
单个还好,多个await就显得麻烦了
利用promise
(async () => {
const [err, data] = await fn().then(data => [null, data] ).catch(err => [err, null])
})()
// 抽离成公共方法
const awaitWrap = (promise) => {
return promise
.then(data => [null, data])
.catch(err => [err, null])
}
const [err, data] = await awaitWrap(fn())
2
3
4
5
6
7
8
9
10
11
# await 能获取到 Promise.reject 抛出的值吗
是的,await
可以获取到 Promise.reject
抛出的值,但它需要用 try...catch
捕获。否则,Promise.reject
会导致一个未处理的异常抛出。而且会阻止后续代码执行。
async function example() {
try {
const result = await Promise.reject("Error occurred");
console.log(result); // 不会执行
} catch (error) {
console.log("Caught:", error); // 输出: Caught: Error occurred
}
}
example();
2
3
4
5
6
7
8
9
10
解释
- 如果
await
遇到一个Promise.reject
,它会抛出该Promise
的拒绝值。 - 我们可以通过
try...catch
来捕获这个值。
如果没有用 try...catch
或者没有为返回的 Promise
设置 .catch()
,会导致未处理的拒绝错误。例如:
async function example() {
const result = await Promise.reject("Error occurred"); // 这里会抛出异常
console.log(result); // 不会执行
}
example(); // 会导致 UnhandledPromiseRejectionWarning
2
3
4
5
6
解决方法: 始终用 try...catch
或为返回的 Promise
添加 .catch()
来处理异常。
# async函数Generator函数的区别
1.内置执行器。
Generator 函数的执行必须靠执行器,而async函数自带执行器。也就是说,async函数的执行,与普通函数一模一样,只要一行。如果你是从上面顺着看下来的,这里的执行器就是Generator和Iterator的yield和next机制,不用怀疑!
2.更好的语义。
async和await,比起星号和yield,语义更清楚了。async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果。
3.正常情况下,await命令后面是一个 Promise 对象。如果不是,会被转成一个立即resolve的 Promise 对象。
4.返回值是 Promise。
async函数的返回值是 Promise 对象,这比 Generator 函数的返回值是 Iterator 对象方便多了。你可以用then方法指定下一步的操作。
← Event-Loop Web Worker →