在promise的学习中,你一定已经了解then()方法的基础,不了解的可以阅读:前端必备:从头开始,搞懂Promise。
但是,除了基础用法,我们必须了解深层次的东西。
首先先来看第一点,也是面试可能会问到的问题。
Promise为什么能够实现异步?
答:Promise会完成控制权的转换。回调能够完成异步,但是什么时候触发回调由其它代码决定,而promise能够将控制权放在自己身上,然后再说js中异步的实现。
回调:使用回调函数来封装程序中的continuation,然后把回调交给第三方(甚至可能是外部代码),然后在合适的时机,这个第三方会调用这个continuation,来实现具体的功能。
控制权:我们通常使用回调来完成传统的异步实现,此时可以很明显的看出什么时候调用这个回调取决于第三方,即控制权在第三方,这样会出现很多问题:缺乏顺序性和可信任性。
所以,我们就需要将控制权从第三方反转回来,即不把continuation传给第三方,而是希望第三方给我们提供它的能力,然后由我们自己决定做什么,这种范式就是promise。
有点像吸星大法,你用我没有的能力帮我做事,但是你老是不按规矩来还不可靠,我生气了,把你的能力吸走,归为己有,然后我自己去做那些事。
接下来看第二点,也是本文的重点,thenable。
Thenable
在promise的使用中,判断某个值是不是真正的promise是很关键的。
你是否有疑问,我用promise构造函数,直接new一个promise,就算是查它的类型,我直接使用instanceof promise来判断不就好了么?这有什么好讲的?
答案是,没这么简单。
在我们常见的promise使用中,至少有3种方式可以创建promise。
-
new promise(),这也是其它文章中说的标准promise。
-
使用非es6原生promise,也就是自己实现的promise时。
-
从其它浏览器窗口接到的promise,这个promise可能与当前窗口不同,所以那种检查方式无法识别promise实例。
所以说,上面说的那种检验方式,不行。
我们需要自己识别。
方法:then()作为promise的配套组件,我们只需要验证有没有then()就可以了,所以,我们提出了thenable的概念。
thenable:任何含有then()方法的对象或函数。
它的结构是不是很像promise,它们都有一个then()。
所以,我们无法找出promise时,就退而求其次,先找类似promise的,也就是thenable。
比如,我们在生活中养宠物,想养一只柴犬,有纯血统的、含有柴犬和其它品种狗狗的血统的、但血统检测很复杂,只能说它们都是柴犬,都在挑选范围内。
但就像无论是哪种血统,都得有柴犬的基本特征才行,检查这些特征,才能知道狗狗是不是柴犬,确定大的范围后,再想办法去确定血统。
同样的,js中也有检查类型,根据一个值的形态(所具有的属性),然后对它的类型做出假定,这种类型检查,一般用术语“鸭子类型”表示。
鸭子类型:如果看起来像鸭子,叫起来像鸭子,那它就是鸭子。
将这个类型检查应用在检查一个值是否为thenable()上也是可以的。
举个例子。
if(p!==null&&(typeof p==="object"||typeof p==="function")&&(typeof p.then==="function")){
console.log("这是一个thenable")
}else{
console.log("这不是一个thenable")
}
这里我们先判断p是否是函数或对象,并且是否有then()方法,满足这些条件,我们将它认定为thenable。
注意:这样做能成功,但是有很大的缺点,如果把一个then()方法加入了函数或对象的原型链中,那么判断当前这个对象有没有then()就是无效的,即使当前的对象没有then(),还是会被识别为thenable。
所以,在编写“鸭子类型”代码时,别忘了先检查原型链。
通过检查thenable,我们可以解决promise中的一些信任问题,promise也提供了一些方法,让我们将thenable彻底变为promise,这样,它的可信任度就变高了。
后续如有补充会接着写在这里~~