前端可能是目前最易理解的手写promiseWord文档格式.docx
- 文档编号:417028
- 上传时间:2023-04-28
- 格式:DOCX
- 页数:20
- 大小:277.44KB
前端可能是目前最易理解的手写promiseWord文档格式.docx
《前端可能是目前最易理解的手写promiseWord文档格式.docx》由会员分享,可在线阅读,更多相关《前端可能是目前最易理解的手写promiseWord文档格式.docx(20页珍藏版)》请在冰点文库上搜索。
let
resolve
()
=>
{
};
失败
reject
立即执行
executor(resolve,
reject);
}
上面的代码实现了Promise构造函数的主体,但有两个问题:
2.executor有可能会出错,对吧,毕竟是用户传进来的方法,类似下面这样。
如果executor出错,报错我们需要用trycatch捕获一下,Promise应该被其throw出的值reject:
new
Promise(function(resolve,
reject)
console.log(a)
a
没有被定义
})
3.resolve、reject还是空函数,我们需要在里面补上逻辑。
接下来继续完善:
Promise(executor){
初始化state为等待态
value
state改变,resolve调用就会失败
if
(this.state
===
)
resolve调用后,state转化为成功态
fulfilled'
储存成功的值
value;
reason
state改变,reject调用就会失败
reject调用后,state转化为失败态
rejected'
储存失败的原因
reason;
如果executor执行报错,直接执行reject
try{
}
catch
(err)
reject(err);
用一张图小结一下:
上面的代码不算特别复杂,下面的then方法有点复杂。
实现then方法
Promise对象有一个then方法,用来注册在这个Promise状态确定后的回调。
当Promise的状态发生了改变,不论是成功或是失败都会调用then方法
then方法使用方法如下:
then
方法传入两个方法作为参数,一个是fn1方法,一个是
方法
p1.then(function
fn1(data){
方法的参数,用于获取promise对象的值
},
fn2(err){
方法的参数,用于获取失败的原因
从上面的例子,很明显,我们得出结论:
2.then方法可以在p1实例上调用。
因此then方法的实现是在Promise的prototype上。
3.then方法会返回一个Promise,而且是返回一个新的Promise(详情)对象。
4.可以多次调用then方法,也就是链式调用,并且每次会返回一个新Promise对象,Promise状态是不确定的,可能是fullfilled,也可能是resolve,取决于那一次调用then时,fn1的返回值。
所以,then方法的实现也很简单,根据Promise状态来调用不同的回调函数即可
下面是then方法的思路图:
下面我们来实现then方法:
then方法接收两个参数,fn1,fn2,分别为Promise成功或失败后的回调
Promise.prototype.then
function(fn1,
fn2)
var
self
this
promise2
首先对入参
fn1,
fn2做判断
typeof
function'
?
:
function(v)
{}
function(r)
(self.status
resolved'
return
promise2
//todo
todo
首先,对入参fn1,fn2做判断。
规范中说,fn1和fn2都是可选参数。
也就是说可以传也可以不传。
传入的回调函数也不是一个函数类型,那怎么办?
规范中说忽略它就好了。
因此需要判断一下回调函数的类型,如果明确是个函数再执行它。
其次,Promise总共有三种可能的状态,我们分三个if块来处理,在里面分别都返回一个newPromise。
所以,接下来的逻辑是:
∙如果promise状态是resolved,需要执行fn1;
∙如果promise状态是rejected,需要执行fn2;
∙如果promise状态是pending,我们并不能确定调用fn1还是fn2,只能先把方法都保存在fn1Callback,fn2Callback数组中。
等到Promise的状态确定后再处理。
根据上面的逻辑,填充下面代码:
把
fn1、fn2
放在
try
里面,毕竟
是用户传入的,报错嘛,很常见
x
fn1(self.data)
执行后,会有返回值,通过
注入到
返回的
promise
中
resolve(x)
(e)
reject(e)
fn2(self.data)
reject(x)
reject(e)
this.fn1Callback.push(function(value){
fn1(self.data);
this.fn2Callback.push(function(value)
fn2(self.data);
∙fn1,fn2都是用户传入的,有可能报错唉,所以要放在trycatch里面
∙fn1,fn2的返回值,我们记为x,规范中的命名也是x,保持一致。
x值将在下文中频繁使用。
then函数本质是把fn1的返回值,包装成一个promise返回出去。
问题是,fn1的返回值是开发者写的,可能千奇百怪。
上面代码中,假定x是一个普通值。
其实实际上,x有不同的情况,我们得去分别处理:
∙如果x是一个普通值,如同上面的代码,直接使用resolve方法,then就可以返回一个正常的promise
Promise((resolve)
∙如果x是一个promise,需要等待这个promise状态变化,拿到fullfilled的值。
然后我们代码再改一改,增加一个判断
(x
instanceof
Promise)
x.then((data)
{resolve(data)},
{reject(e)})
else
∙根据规定,我们需要兼容各种百花齐放的写法,比如说,如果x是一个对象,并且对象有then方法,也就是所谓的thenable对象,则我们得这样处理:
(typeof
x.then
){
x.then(function(y){
resolve(y)
function(e){
})
上面,我们新增了一些逻辑,为了处理x返回值的各种情况。
我们需要把这些逻辑,挪到一个resolvePromise方法中,resolvePromise负责把各种稀奇古怪的x包装成一个正常的promise。
resolvePromise
resolvePromise方法,就是为了把x包裹成一个正常的promise
resolvePromise(promise2,
x,
resolve,
为了防止循环引用
(promise2
x)
reject(new
TypeError('
Chaining
cycle
detected
for
promise!
));
如果
是
promise
x.then(function
(data)
resolve(data)
});
return;
object
类型或者是
function
((x
!
==
null)
&
((typeof
object'
||
)))
拿x.then可能会报错
先拿到
x.then
x.then;
called
这里的写法,是
then.call(this,
fn2)
then.call(x,
(y)
//called是干什么用的呢?
有一些
实现的不是很规范,瞎搞的,比如说,fn1,
本应执行一个,
但是有些then实现里面,fn1,
fn2都会执行
为了
和
只能调用一个,
设置一个
called
标志位
(called)
true;
y,
(r)
reject(r);
resolve(x);
reject(e);
上面代码,需要注意的地方:
∙varthen=x.then这一行代码可能会报错,需要用trycatch包一下。
为什么取对象上的属性有报错的可能?
Promise有很多实现(bluebird,Q等),Promises/A+只是一个规范,大家都按此规范来实现Promise才有可能通用,因此所有出错的可能都要考虑到,假设另一个人实现的Promise对象使用Object.defineProperty()恶意的在取值时抛错,我们可以防止代码出现Bug。
∙如果对象中有then,且then是函数类型,就可以认为是一个Promise对象,之后,使用x作为this来调用then方法。
∙如果x===promise2,则是会造成循环引用,自己等待自己完成,则报“循环引用”错误。
x和promise2是同一个是什么情况呢?
p2
p1.then(function(data){
console.log(data)
p2;
上面的例子中,p1.then()的返回值是p2,fn1的返回值也是p2。
这会存在什么问题呢?
promise如果不手动的调用resolve方法,是没有办法修改状态的。
p2的状态没法改变,没法自己改动自己的状态,永远不会被fullfilled、rejected
∙我们需要不同的Promise实现能够相互交互,即我们要把fn1/fn2的返回值,x,当成一个可能是Promise的对象,也即标准里所说的thenable,并以最保险的方式调用x上的then方法。
如果大家都按照标准实现,那么不同的Promise之间就可以交互了。
而标准为了保险起见,即使x返回了一个带有then属性但并不遵循Promise标准的对象(比如说这个x把它then里的两个参数都调用了,同步或者异步调用(PS,原则上then的两个参数需要异步调用,下文会讲到),或者是出错后又调用了它们,或者then根本不是一个函数),也能尽可能正确处理。
then里面的函数需要异步执行
最后,我们刚刚说到,原则上,promise.then(onResolved,onRejected)里的这两相函数需要异步调用,关于这一点,标准里也有说明:
Inpractice,thisrequirementensuresthatonFulfilledandonRejectedexecuteasynchronously,aftertheeventloopturninwhichtheniscalled,andwithafreshstack.
那么如何将同步代码变成异步执行呢?
可以使用setTimeout函数来模拟一下:
setTimeout(()=>
//此入的代码会异步执行
},0);
利用此技巧,将代码then执行处的所有地方使用setTimeout变为异步即可,举个栗子:
setTimeout(()
fn1(value);
promise主体结构
1.
定义
status
状态
2.
的数组
3.
4.
executor
执行
this;
self.status
self.fn1Callback
self.fn2Callback
做到事情
修改this
实例的状态
这里的data
遍历执行
this
fn1Callback
上挂载的方法
resolve(value)
(value
value.then(resolve,
异步执行所有的回调函数
self.data
(let
i
0;
<
self.fn1Callback.length;
i++)
self.fn1Callback[i](value);
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 前端 可能 目前 理解 手写 promise