接着上面两篇笔记归纳下Promise的学习笔记
同步异步执行顺序
注意:在Promise构造函数的内部,执行顺序是同步的。等内部执行完成后,再选择调用then
或catch
方法,then方法执行是异步的。
1 | console.log(1); |
注意:
Promise构造函数的回调函数(参数)是同步执行的,它的then()
是异步任务resolve()
后p状态改变,相当于调用了then()
的参数函数,** p.then()
是异步任务,所以在本轮事件循环中所有同步任务执行完以后才会执行,所以5在最后打印**
把同步任务转为异步任务
72-74:创建一个函数createAsyncTask()
用于将传入的同步任务syncTask
转换为已经决议为成功的Promise实例,由于已成功的状态,该实例会立即调用then()的第一个回调函数:把syncTask
传给syncTask()
并立即执行syncTask()
。
76-81:调用这个函数createAsyncTask()
,往里传入一个thenable对象作为参数。
那么这个参数就会使用Promise.resolve()
将自己变成 Promise实例,这个Promise实例执行完后返回2并成为已成功的状态,所以就会触发then()的第一个回调函数,该回调函数接收Promise实例返回的2作为参数:
1 | function createAsyncTask(syncTask) { |
createAsyncTask 的流程如下:
- 首先,将同步函数
syncTask
包装在Promise.resolve
中,创建一个已解决的 Promise 实例,并将其添加到微任务队列中。 - 当前任务执行完成后,微任务队列中的 Promise 实例会触发它的成功状态,并执行
.then
方法中的回调函数。 - 回调函数中的
result
参数是前面包装的syncTask
函数,然后通过result()
的调用来执行这个函数。
注意:虽然 syncTask
本质上是同步函数,但由于它被包装在 Promise.resolve
中,它的执行会被推迟到微任务队列中,从而在当前任务完成后执行。
需要了解:
当你调用 Promise.resolve(value)
时,value 的解析会被放入微任务队列,而不是像普通的Promise定义函数一样立即执行
当你在 Promise 中返回一个值,无论是直接的值还是通过 resolve()
返回,这个值都会被传递给 then
方法注册的回调函数
- 首先,同步任务执行,打印出
"我是同步任务!"
。 - 然后,
createAsyncTask
函数内部的回调函数执行,打印出"我变成了异步任务! ! !"
。随后,它返回1 + 1
的结果,即2
。 - 接着,Promise 实例进入成功状态,触发了
.then()
注册的回调函数,将2
作为参数传递给回调函数,然后打印出2
。
小案例
可以看到3张图片的加载时间是不同的,而是在最后一张图片加载完成后页面才显示出图片:
注意
事件函数绑定时不能使用img.onload = resolve(img);
这样图片还没加载完成,在赋值阶段函数resolve(img)
就直接被调用了。
检验错误
可以增加一个检验错误,多往数组中放入一个src
:
结果就会返回我们在loadImg()
中的onerror()
中返回的错误对象e:
总结
- Promise 改善了传统回调造成的代码难维护、控制反转的问题。
- Promise是异步的,但Promise构造函数中代码的执行时同步的。
- 如果
Promise.all()
接收的是空数组,那么马上会被决议为成功。 - 如果
Promise.race()
接收的是空数组,那么会永远挂起。 catch()
返回的Promise实例,可以无线捕获错误问题(链式)。
Promise.resolve方法的传参
- 如果 resolve() 接收的是普通的值,就会立即被决议为成功,直接传递这个值。
- 如果 resolve() 接收的是一个Promise实例,则返回这个Promise实例。
- 如果 resolve() 接收的是一个thenable对象,则会把该对象包装成Promise实例,并立即执行该thenable对象的
then()
Promise.reject方法的传参
Promise.reject()会产生一个决议失败的Promise实例,并直接传递接收到的参数。(不会有任何处理,假设参数是thenable对象,那么thenable对象也会被直接作为错误信息传递例子)