你不知道的JavaScript----promise

2023-11-02

目录

什么是Promise

Promise

Promise 值

完成事件

Promise “事件”

具有 then 方法的鸭子类型

Promise 信任问题

调用过早

调用过晚

Promise 调度技巧

回调未调用

调用次数过少或过多

未能传递参数 / 环境值

吞掉错误或异常

是可信任的 Promise 吗?

链式流

Promise API

Promise 用法

Promise.all 和 Promise.race 的区别和使用

Promise - finally

Promise 局限性

顺序错误处理

单一值

单决议

惯性

无法取消的 Promise

Promise 性能


什么是Promise

通过如下场景来阐述:

我到快餐店的柜台,点了一个芝士汉堡。通过下订单并付款,我已经发出了一个对某个值(芝士汉堡)的请求。我已经启动了一次交易。但是,通常我不能马上就得到这个汉堡。收银员会交给我某个东西来代替汉堡:一张带有订单号的收据。收据就是一个 IOUI owe you,我欠你的)承诺(promise),保证了最终我会得到我的汉堡。
所以我得好好保留我的收据,我知道这代表了我未来的汉堡,所以不需要担心, 只是现在我还是很饿!
在等待的过程中,我还可以做点其他的事情,我脑海中已经在想着未来的芝士汉堡了,尽管现在我还没有拿到手。我的大脑之所以可以这么做,是因为它已经把订单号当作芝士汉堡的占位符了。 从本质上讲,这个占位符使得这个值不再依赖时间。这是一个未来值
终于,我听到服务员在喊“订单 113”,然后愉快地拿着收据走到柜台,把收据交给收银 员,换来了我的芝士汉堡。换句话说,一旦我需要的值准备好了,我就用我的承诺值(value-promise)换取这个值本身。
但是,还可能有另一种结果。他们叫到了我的订单号,但当我过去拿芝士汉堡的时候,收银员满是歉意地告诉我:“不好意思,芝士汉堡卖完了。”除了作为顾客对这种情况感到愤怒之外,我们还可以看到未来值的一个重要特性:它可能成功,也可能失败。

现在值与将来值——-——————

不要小瞧 x + y

var x, y = 2; 
console.log( x + y ); // NaN

运算 x + y假定了 xy 都已经设定。也就是说在这里我们假定了 xy 的值都是已决议的,那么试想如果其中一个值未决议(是未来值)就进行计算,那会怎么样?
如果有的语句现在完成,而有的语句将来完成,那就会在程序里引起混乱。例如:如果语句 2 依赖于语句 1 的完成,那么就会有两个输出:要么语句 1 马上完成,一切 顺利执行;要么语句 1 还未完成,语句 2 因此也将会失败。
x + y为例,如果它们中的任何一个还没有准备好,那我们就等待两者都准备好。一旦可以就马上执行加运算。下面是通过回调的实现:

function add(getX,getY,cb) { 
    var x, y; 
    getX( function(xVal){ 
        x = xVal; 
        // 两个都准备好了?
        if (y != undefined) { 
             cb( x + y ); // 发送和
        } 
    } ); 
    getY( function(yVal){ 
        y = yVal; 
        // 两个都准备好了?
        if (x != undefined) { 
            cb( x + y ); // 发送和
        } 
    } ); 
} 
// fetchX() 和fetchY()是同步或者异步函数
add( fetchX, fetchY, function(sum){ 
    console.log( sum ); // 是不是很容易?
} );

上面我们把 xy 都当作未来值来处理。在我们不确定某个值是现在值还是将来值的时候,我们就把它当做将来值来处理,这样可以防止很多意外的发生。说得直白些就是,为了统一处理现在和将来,我们把它们都变成了将来,即所有的操作都成了异步的。

Promise

Promise

先来看看,通过 Promise 函数表达 x + y 的例子:

function add(xPromise,yPromise) { 
    return Promise.all( [xPromise, yPromise] ) 
    .then( function(values){ 
        return values[0] + values[1]; 
    } ); 
} 
// fetchX()和fetchY()返回相应值的promise
add( fetchX(), fetchY() ) 
.then( function(sum){ 
    console.log( sum );
} );

这样就简单多了。Promise 的决议结果可能是拒绝而不是完成。拒绝值和完成的 Promise 不一样:

完成值总是编程给出的,而拒绝值,通常称为拒绝原因,可能是程序逻辑直接设置的,也可能是从运行异常隐式得出的值

通过 Promise,调用 then(..)实际上可以接受两个函数,第一个用于完成情况,第二个用于拒绝情况:

add( fetchX(), fetchY() ) 
.then( 
    // 完成处理函数
    function(sum) { 
        console.log( sum ); 
    }, 
    // 拒绝处理函数
    function(err) { 
        console.error( err );
    } 
);

我们清晰的看见,Promise采用了分离式回调。Promise 是一种封装和组合未来值的易于复用的机制。

注意: 关于 Promise 需要理解的最强大也最重要的一个概念:一旦 Promise 决议,它就永远保持在这个状态,成为了不变值。(一旦改变不能在改变 承诺者模式)

完成事件

从另外一个角度看待 Promise 的决议:一种在异步任务中作为两个或更多步骤的流程控制机制,时序上的 this-then-that
假定要调用一个函数执行某个任务,这个函数可能立即完成任务,也可能需要一段时间才能完成。我们只需要知道它什么时候结束,这样就可以进行下一个任务。即我们想要通过某种方式在程序完成的时候得到通知。
侦听某个通知,我们就会想到事件,我们的脑海中可能会出现如下的伪代码:

foo(x) { 
 // 开始做点可能耗时的工作
} 
foo( 42 ) 
on (foo "completion") { 
 // 可以进行下一步了!
} 
on (foo "error") { 
 // 啊,foo(..)中出错了
}

当然这样的代码,Javascript并不提供,更自然的表达方法是:

function foo(x) { 
 // 开始做点可能耗时的工作
    return listener; // 构造一个listener事件通知处理对象来返回
} 
var evt = foo( 42 ); 
evt.on( "completion", function(){ 
 // 可以进行下一步了!
} ); 
evt.on( "failure", function(err){ 
 // 啊,foo(..)中出错了
} );
bar( evt );  // 让bar(..)侦听foo(..)的完成

这里没有把回调传给 foo(..),而是返回一个名为 evt的事件注册对象,由它来接受回调。此处的反转显而易见,我们通过反转再反转,拿回了对代码的控制权,即调用代码将控制权反转给第三方,再从第三方那里反转回来。对控制反转的恢复实现了更好的关注点分离,即bar不需要关注foo(...)的调用细节,foo(..)也不需要关注bar是否存在。从本质上说,evt 对象就是分离的关注点之间一个中立的第三方协商机制。

Promise “事件”

其实上面的事件侦听对象 evt 就是 Promise 的一个模拟。foo(..)bar(..)的内部实现或许如下

function foo(x) { 
    // 可是做一些可能耗时的工作
    // 构造并返回一个promise
    return new Promise( function(resolve,reject){ 
        // 最终调用resolve(..)或者reject(..)
        // 这是这个promise的决议回调
    } ); 
}
function bar(fooPromise) { 
    // 侦听foo(..)完成
    fooPromise.then( 
        function(){ 
            // foo(..)已经完毕,所以执行bar(..)的任务
        }, 
        function(){ 
            // 啊,foo(..)中出错了!
        } 
    ); 
}

注意: 传入Promise的函数会立即执行,不会像 then(..) 中的回调一样异步延迟

另一种实现方式:

function bar() { 
 // foo(..)肯定已经完成,所以执行bar(..)的任务
} 
function oopsBar() { 
 // 啊,foo(..)中出错了,所以bar(..)没有运行
} 
// 对于baz()和oopsBaz()也是一样
var p = foo( 42 ); 
p.then( bar, oopsBar ); 
p.then( baz, oopsBaz );

注意: p.then( .. ).then( .. )p.then(..); p.then(..); 是两个意义,前者p决议后调用then,因为.then返回的总是Promise,所以才能支持链式调用,此时第二个.then用的是第一个.then返回的Promise的决议值,而后者用的都是p的决议值。

具有 then 方法的鸭子类型

判断类似于 Promise 的值是否是真正的 Promise 很重要,多见于 Promise.resolve() 对于 Promisethenable 的展开。 thenable 类似于 Promise,指任何具有 then(..) 方法的对象和函数。

注意: 对象的原型链上若具有 then(..) 方法,那么这个对象也会被识别为 thenable

这里主要讲对thenable的类型检查。

根据一个值的形态(具有哪些属性)对这个值的类型做出一些假定。这种类型检查一般用术语鸭子类型来表示——“如果它看起来像只鸭子,叫起来像只鸭子,那它一定就是只鸭子”。

thenable值的鸭子类型检测就大致如下:

if ( 
   p !== null && 
   ( 
       typeof p === "object" || 
       typeof p === "function" 
    ) && 
       typeof p.then === "function" 
) { 
   // 假定这是一个thenable! 
} 
else { 
   // 不是thenable 
}

注意

   如果有其它代码无意或恶意地给Object.prototype、Array.prototype或者其它原生原型添加了then方法,也会造成灾难。不过鸭子类型有时候还是有用的,只是要小心鸭子类型把不是Promise的值误判为Promise的情况。

Promise 信任问题

Promise 的特性就是专门用来为回调编码的信任问题提供一个有效的可复用的答案。

调用过早

即使是立即完成的 Promise也无法被同步观察到。即对一个 Promise 调用 then(..) 的时候,即使这个 Promise 已经决议,提供给then(..) 的回调也总会被异步调用(微队列)。所以 Promise 不存在调用过早这个问题。(.then异步微任务)

调用过晚

Promise 创建对象调用 resolve(..)reject(..) 时,这个 Promisethen(..) 注册的观察回调就会被自动调度。可以确信,这些被调度的回调在下一个异步事 件点上一定会被触发。所以也不存在调用过晚的问题。

注意: 当 Promise 决议后,其上所有的通过 then(..) 注册的回调都会在下一个异步时机点上依次被立即调用。这些回调中的任意一个都无法影响或延误对其他回调的调用。
举个

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

你不知道的JavaScript----promise 的相关文章

随机推荐

  • 密码方向国家相关政策(含等保2.0)

    听说目前业内都有这样一个基本共识 不做等保 违法 不做密码测评 违法 2018年2月国家密码管理局发布 GM0054 2018 信息系统密码应用基本要求 从信息系统的物理和环境安全 网络和通信安全 设备和计算安全 应用和数据安全四个层面提出
  • 苹果笔记本怎么查看计算机基本信息,怎么区分查找苹果电脑笔记本macBook pro的年份、型号及序列号...

    苹果笔记本每年都更新 新的产品在配置和大小等都有所不同 然而 每年都叫MacBook 那么 如何区别自己的MacBook是哪一年生产的 怎么查看具体型号以及序列号呢 识别自己的苹果笔记本的Mac 机型也很重要 因为查看保修保障范围 创建个人
  • 解决双系统出现的windows时间错误

    首先确定Ubuntu系统的时间正确 查看系统时间 date R 我需要的是东八区 而这里显示的是 0500 很明显不对 因此 1 运行tzselect tzselect 在这里我们选择亚洲 Asia 确认之后选择中国 China 最后选择北
  • 定时每天凌晨一点在linux系统上执行一个autobuild.sh脚本如何实现?

    定时每天凌晨一点在linux系统上执行一个autobuild sh脚本如何实现 可以使用linux的计划任务功能crontab来实现定时执行脚本 具体步骤如下 编辑crontab计划任务列表 bash crontab e 这会打开一个文本编
  • Android实现Activity的跳转(Android学习笔记2)

    Android实现Activity的跳转 一 创建新的Activity 二 设计主界面和菜单界面 三 实现Activity的跳转 1 显示意图跳转Activity的三种方式 1 1 方式一 1 2 方式二 1 3 方式三 2 隐式意图跳转A
  • 【安全研究】从mimikatz学习Windows安全之访问控制模型(三)

    作者 Loong716 Amulab 0x00 前言 在之前的文章中 分别向大家介绍了Windows访问控制模型中的SID和Access Token 本篇文章中将为大家介绍最后一个概念 特权 Windows操作系统中许多操作都需要有对应的特
  • Antv G2plot学习笔记(一)

    Antv G2plot学习笔记 一 官方网址 https g2plot antv vision zh 在执行官方的实例中 发现无法将数据进行图表展示 经过好友的分享和实践发现是出在变量引用不到的问题 之前的const linePlot ne
  • opencv2与opencv的不同

    一 Opencv2与opencv1的区别 Opencv1 0版本于2006年面世 主要基于C语言 2009年发布opencv2 主要基于C 此时OpenCV库被划分成多个模块 这些模块被编译成库文件后 位于lib文件夹中 主要有以下模块 版
  • AIX 文件 打包 与 压缩 tar gzip compress 的使用

    今天在Aix用tar cvf 备份 打成tar包 占有硬盘空间过大 没有压缩比 尝试使用tar zcvf linux系统下可以用 z 命令 z 用gzip来压缩 解压缩文件 加上该选项后可以将档案文件进行压缩 但还原时也一定要使用该选项进行
  • (Visual Grounding 论文研读) Pseudo-Q: Generating Pseudo Language Queries for Visual Grounding, 2022 CVPR

    最近在看关于visual grounding的文章 对于文章中理解不恰当的内容欢迎批评指正 本文将根据论文的结构来组织结构并且展开一定的拓展 Abstract visual grounding VG 即根据自然语言查询在图像中定位对象 是视
  • m3u8加密文件原理及下载脚本

    一 加密ts文件解密 EXTM3U EXT X VERSION 3 EXT X MEDIA SEQUENCE 0 EXT X ALLOW CACHE YES EXT X TARGETDURATION 13 EXT X KEY METHOD
  • GBASE 8s 表分片

    表分片 技术允许在表一级对数据存储进行控制 用户可以对表中的记录或索引进行分组 并且存储在不同的位 置 这样可以将数据存储到多个磁盘上 从而减少对磁盘I O的竞争 数据分片的方案以及分片数据所存放的一组 dbspace构成了 分片策略 数据
  • Canvas对ImageData进行Resize操作(平滑高性能处理)

    问题背景 通过getImageData函数得到的ImageData通过putImageData重新放到canvas容器无法进行resize操作 如果通过toDataURL函数转为Image再使用drawImage函数性能太差 解决代码 处理
  • 数据科学编程技能

    特点 使用数据科学技术 您可以将原始数据转化为可操作的见解 适用于从城市规划到精准医学的各个领域 数据科学编程技能汇集了您入门所需的所有基础技能 即使您没有编程或数据科学经验 指导安装和配置解决专业级数据科学问题所需的工具 包括广泛使用的
  • 配置CentOS8 yum镜像源

    配置yum镜像主要修改三个文件 文件位置 etc yum repos d CentOS Linux AppStream repo 将上面的两段代码注释掉 之后添加清华镜 清华云镜像地址 baseurl https mirrors tuna
  • 【问题记录】pytorch自定义数据集 No such file or directory, invalid index of a 0-dim

    保存模型 保存整个神经网络的结构和模型参数 torch save mymodel mymodel pkl 只保存神经网络的模型参数 torch save mymodel state dict mymodel params pkl 导入模型
  • [Linux]Ubuntu下idea的idea64.vmoption文件

    换了ubuntu环境开发 动了help gt Edit custom Vm options的文件 导致idea无法打开 解决办法 删除 root config JetBrains IntelliJIdea2022 1 idea64 vmop
  • [电动智能汽车-4]:原理 - 高压电源系统与互锁系统

    目录 第1章 高压电源系统概述 1 1 高压电源系统原理图 1 2 高压电源系统连接图 1 3 互锁 第2章 动力电池 2 1 安装位置 2 2 动力电池的外观 2 3 动力电池的组成 2 4 电芯的类型 2 5 电池包的参数 2 6 高压
  • Docker的Compose规范现已成为开放标准

    由Docker创建的用于定义多容器应用程序的系统Docker Compose现在将作为开放标准进行开发 称为新标准的Compose规范旨在允许Compose创建的应用程序在Kubernetes和Amazon Elastic Containe
  • 你不知道的JavaScript----promise

    目录 什么是Promise Promise Promise 值 完成事件 Promise 事件 具有 then 方法的鸭子类型 Promise 信任问题 调用过早 调用过晚 Promise 调度技巧 回调未调用 调用次数过少或过多 未能传递