美文网首页
【中卷】你不知道的JavaScript(二)

【中卷】你不知道的JavaScript(二)

作者: 林思念 | 来源:发表于2021-10-31 17:24 被阅读0次

一、Promise

在promise的学习中

但是,除了基础用法,我们必须了解深层次的东西。

首先先来看第一点,也是面试可能会问到的问题。

Promise为什么能够实现异步?
答:Promise会完成控制权的转换。回调能够完成异步,但是什么时候触发回调由其它代码决定,而promise能够将控制权放在自己身上,然后再说js中异步的实现。

  • 回调:使用回调函数来封装程序中的continuation,然后把回调交给第三方(甚至可能是外部代码),然后在合适的时机,这个第三方会调用这个continuation,来实现具体的功能。

  • 控制权:我们通常使用回调来完成传统的异步实现,此时可以很明显的看出什么时候调用这个回调取决于第三方,即控制权在第三方,这样会出现很多问题:缺乏顺序性和可信任性。

所以,我们就需要将控制权从第三方反转回来,即不把continuation传给第三方,而是希望第三方给我们提供它的能力,然后由我们自己决定做什么,这种范式就是promise。

function ( x ){
  // 耗时操作
  return listener
}
var evt = foo( 42 )
evt.on("complete", function( ){ 
  // next 
})
evt.on("error", function( ){
  // 出错了。。。
})

二、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,这样,它的可信任度就变高了。

三、Promise信任问题

只用会掉编码的信任问题,把一个会掉传入工具(三方)时,可能会出现的问题:

  • 调用过早
  • 调用过晚(或不被调用)
  • 调用次数过少或过多
  • 吞掉可能出现的错误和异常

Promsie 的特性就是专门针对这些问题提供一个有效的可复用的答案
1、调用过早
这个问题主要是担心代码是否会引入这样的问题。一个任务有时同步完成有时异步完成,可能会导致竞态条件。Promise 即使是立即完成的promise,也无法被同步观察到
2、调用过晚
和前面一点类似,Promise 创建对象调用resolve(...) 或 reject(...)时,这个promise 的then(...)注册的观察回调就会自动调度,可以确信,这些调度的回调在下个异步事件点上一定会触发

p.then(function(){
  p.then(function(){
    console.log("C")
  })
  console.log("A")
})
p.then(function(){
  console.log("B")
})
// A B C

C无法抢占B,这是promise的运作方式
3、回调未调用
没有任何东西可以阻止(包括js错误)promise 决议,promise总会调用成功和拒绝回调中的一个
4、调用次数过少或过多
promsie 只能被决议一次,一旦状态发生改变就不会更改,回调次数和调用次数保持一致
5、吞掉错误或异常
promise 有自己的异常处理部分,但是有种情况是检测不到的

var p = new Promise(function(resolve, reject){
  resolve(42)
})
p.then(
  function fulfilled(msg){
    foo.bar() // 未定义方法
    console.log(msg)  // 永远不会到达这里
  },
  function rejected(err){
    console.log(err)
    // 不会到达这里
  }
);

你可能会说加个catch函数不就好了吗?是可以,但是catch中的异常处理由谁来捕获呢?

var p = new Promise(function(resolve, reject){
  resolve(42)
})
p.then(
  function fulfilled(msg){
    foo.bar() // 未定义方法
    console.log(msg)  // 永远不会到达这里
  },
  function rejected(err){
    console.log(err)
    // 不会到达这里
  }
).catch(err=>{
  console.log(err)
  typeof a
  let a =  1
})

后面继续添加catch 或者 finishily?还是会有同样的问题,同时这个问题并不是promise吞掉了错误异常,而是逻辑问题

Promise.resolve
let p1 =  Promise.resolve(42)
let p2 =  Promise.resolve(42)
p1 === p2 // false

如果向promise.resolve传递一个真正的Promise,就会返回同一个Promise

var p1 = Promise.resolve(42)
var p2 = Promise.resolve(p1)
p1 === p2  // true
如果是thenable类型的非promise,该怎么执行呢?

符合预期

var p = {
  then : function(cb){
    cb(42)
  }
}
p.then(
  function fulfilled(val){
    console.log(val) // 42
  },
  function rejected(err){
    // 不会到达这里  
  }
)

不符合预期

var p = {
  then : function(cb, errcb){
    cb(42)
    errcd('evil laugh')
  }
}
p.then(
  function fulfilled(val){
    console.log(val) // 42
  },
  function rejected(err){
    console.log(err)   // 'evil laugh'
  }
)

Promise.resolve(...)是可以接收任何 thenable 的值,然后将其解封为他的非 thenable 值,从Promise.resolve(...)得到一个真正的Promise,是一个可信任的值

Promise.resolve(p)
  .then(
    function fulfilled(val){
      console.log(val)   // 42
    },
    function rejected(err){
      // 永远不会到到达这里
    }
  )

相关文章

网友评论

      本文标题:【中卷】你不知道的JavaScript(二)

      本文链接:https://www.haomeiwen.com/subject/amwoaltx.html