JavaScript和promise——0_1 promise

news2024/12/28 21:02:31

文章目录

  • 是什么?
  • 未来值
    • 回调和未来值
      • 在回调环境下这么和未来值交互?
      • 群居的未来值
        • 其他的解决方案
      • 这样写可以实现目标效果。可是,这样写优雅吗?
    • 英雄登场
      • 关键词:then
      • 关键词:回调
  • 为什么promise不需要start函数
  • promise和不靠谱的回调
    • 不靠谱的回调
      • 不被信任的回调,到底会做出些什么诡异操作?
      • 这些问题是怎么出现的?
        • 调用回调过早
        • 调用回调过晚
        • 回调次数不确定(甚至可能是0次)* & *未能传递所需的环境和参数
        • 异常丢失
    • Promise的解决方案
      • *调用回调过早*
      • *调用回调过晚*
      • *回调次数不确定*
      • 未能传递所需的环境和参数
      • 异常丢失
  • 为什么promise值得被信任
      • 那为什么promise就比回调更值得被信任呢?

天不生Promise,则JavaScript万古如长夜

是什么?

Promise是JavaScript的一种替换回调的解决方案,绝大多数情况下,是在程序出现异步任务的时候使用他。他诞生于社区,原先只是一种约定俗成的范式,直到 ES6 提供了原生Promise对象的实现,至此Promise成为了JavaScript的语言标准




未来值

在描述Promise是如何改变JavaScript的异步生态之前,我们需要先理解一个概念——未来值

未来值是一个凭证,他意味着某个在将来的某个时间点程序将会获取到的一个值。
试想一下,如果没有这个值的存在,你依然需要异步回调吗?
答案是否定的


这样你会说,不对啊。那串行的动画,难道就不是没有未来值而需要异步的动作吗?一串连贯的动画之间存在顺序关系,但是动画和主线程之间是并行的

如果你这么想,那说明你的问题很严重,因为:

JavaScript中只有不在此时立刻开始的异步任务,而不存在并行的任务。因为JavaScript引擎是基于事件循环实现的。他永远是单线程的!


所以我们讨论的前提,就是我们面对的并不是多线程,而是一段不知道会在将来哪个时间点突然执行完成的异步任务。正因为这种不确定性,所以我们需要一个机制,来确保无论这个异步任务在什么时候执行完毕,程序都可以按照我们所预想的那样执行

通过这个不确定执行时间的异步任务,有时候我们可以得到一个返回值,这个值,我们就把他称为未来值


我们的异步回调,很明显就需要围绕这个不确定什么时候会出现的未来值去操作。而且很显然,我们在回调中需要获取这个未来值,并操作他



回调和未来值

未来值的概念是从JavaScript诞生一来就一直存在的了。比如Ajax中服务器返还的内容,某个定时任务中被修改的状态。就像我们之前说的那样,在promise出现之前,回调是我们处理异步的唯一方式


在回调环境下这么和未来值交互?

回调采用的是一种类似托孤的方式。由于JavaScript引擎没有时间的概念,也没办法直接和宿主平台中的内容打交道。所以JavaScript在把异步任务提交给宿主之后,JavaScript引擎是不知道这段代码会在什么时候,怎么被执行的。而要在未来值出现后回调的函数里面的内容也会在提交异步任务的时候一起被提交给宿主,由宿主在调用了异步任务后,自己去调用回调函数

但是我们知道,宿主调用回调的过程是不值得被信任的.


群居的未来值

一个很关键的问题,未来值未必是孤立的。这个未来值可能会和某个已有的变量有关联;最极端的状况下, 也许未来值会和某个其他未来值存在关联


试想这样一个需求,我们有x和y两个字段,这两个字段我们都需要各自通过一个耗时操作才能得到他们。
最终,在回调函数中,我们需要把x和y相加,得到z。

  1. 我不希望程序因为等待这两个耗时操作而让程序假死,所以获取x和y的动作一定是异步的,而x和y分别是这两个异步任务的返回值(也就是未来值)
  2. x和y各自有不同的异步任务,所以也有不同的回调函数。在两个函数中要共享相同的变量,你必须要升域,把x和y定义于两个回调函数之外,就像这样:
//采用回调的方式实现 x和y相加
function fetchX(xCallback){
    //通过一个耗时操作得到X
    setTimeout(function(){
        xCallback(10);
    },Math.floor(500 * Math.random()));
}

function fetchY(yCallback){
    //通过一个耗时操作得到y
    setTimeout(function(){
        yCallback(5);
    },Math.floor(500 * Math.random()));
}

function add(getX,getY){
    let x,y;

    //回调函数定义
    function xCallback(value){
        x = value;

        if(y !== undefined){
            console.log("我是通过xCallback得到的结果=" + (x + y));
        }
    }

    function yCallback(value){
        y = value;
        if(x !== undefined){
            console.log("我是通过yCallback得到的结果=" + (x + y));
        }
    }

    //调用
    getX(xCallback);
    getY(yCallback);
}

在实际执行的过程中,你会发现光升域是不够的。因为最后的相加动作,必须是在x和y的任务都执行完的情况下才可以执行。那在每一个回调函数里面,我们都需要判断另一个变量是否已经有值了(相当于判断另一个异步任务是否已经执行完了),然后再决定是否继续执行下一步 以此来实现异步任务之间的通讯

其他的解决方案

除了通过在回调函数里面通过异步任务之间的协作的方法意外以外,你还有第二种解决方案。即 链状的回调
简单的来说就是在获取x的回调中触发y的异步任务。这样你就可以保证在y的异步任务完成的时候,x和y一定都是有值的


这样写可以实现目标效果。可是,这样写优雅吗?

现在还只是x和y,如果是N个对象之间的联动,那岂不是妥妥的回调地狱?



英雄登场

很显然,上面那个问题的答案是否定的。于是,promise登场了
上文的处理方式,虽然可以完成我们的需求。但是他总让我们感觉很复杂,因为我们要在每个回调里面都要去判断可能出现的所有情况。


那有没有可能我们把情况统一呢?
既然我这么问了,那答案就一定是肯定的。对于异步任务来说,可能出现的情况无非就两种,完成 or 未完成


在promise中,我们把 所有的情况都视为未完成,视为将来要发生的事情。用promise来表达刚刚那个例子,那他长这样:

function addByPromise(xPromise,yPromise){
    return Promise.all([xPromise,yPromise]);
}

addByPromise(new Promise(function(resolve,reject){
    setTimeout(function(){
        resolve(10);
    },1000);
}),new Promise(function(resolve,reject){
    setTimeout(function(){
        resolve(5);
    },1000);
}))
.then(function(values){
    //当程序走到这里的时候,事实上JavaScript引擎已经取回了程序的执行权
    console.log("使用Promise实现加法同步,结果为:" + (values[0] + values[1]));
});

关键词:then

显然,在then的回调函数里面的那个value参数,就是我们上面聊的【未来值】。而这个【未来值】其实也就是promise的本质。
promise翻译过来的意思是承诺。而value对于then中的回调函数来说,相当于一个对【未来值】的承诺。promise可以保证你写入的then回调,一定会被执行,并获取到正确的【未来值】。

Promise是怎么做到的呢?


关键词:回调

注意到了吗,我刚刚又提到了这个关键字,回调。

没错,在promise中也有回调。那你就会说了,既然promise中有回调,那就必然会出现因为回调而存在的问题(回调地狱且不提,单说宿主可信任程度的问题)

是的,是存在这些问题,但是promise帮我们解决了。怎么解决的呢?这就涉及到了promise的两个重要特性:状态不可逆&值不可变
promise有三种状态,而相同的api,会根据promise当前状态的不同回应不同的行为:

  1. pending:进行中

    promise一被创建出来,就是pending状态。fulfilled和rejected是同级的,一次异步任务只能是fulfilled/rejected

  2. fulfilled:已成功

    promise的状态是 不可逆 的,只要被切换成fulfilled/rejected之后,就不可能再回到pending中

  3. rejected:已异常

托孤式 的回调不同,promise的回调只做一件事:

  • 如果,异步任务成功执行,并拿到【未来值】,把【未来值】传给JavaScript引擎,引擎会把promise的状态从pending转换成fulfilled
  • 如果,异步任务执行失败,并抛出异常,把异常传给JavaScript引擎,引擎会把promise的状态从pending转换成rejected

无论是哪个分支,当JavaScript引擎拿到 【未来值】or 异常 的时候,他就会重新获得对这个任务的主导权。

接下来JavaScript引擎会把接收到的【未来值】 or 异常 传给promise的回调中。注意,这里的回调就不用发送给宿主了,因为这里不存在异步任务了。而是由JavaScript引擎管理的回调




为什么promise不需要start函数

细心点你可能会发现,在我们创建promise的时候,其实异步任务就已经开始执行了。


那为什么我们不需要担心我们还没有写入then回调的时候,异步任务就执行完的情况呢?
在Java、C#这些语言的多线程中,我们可是习惯了先把所有的准备动作都做好再开始线程的做法啊?

  • 第一个原因
    你注意到了吗,promise并不是自动执行then回调的,他用了一个resolve函数,然你在异步任务里面显式的调用then回调

  • 第二个原因
    promise的内部构造显然是参考了【状态(State)】的设计模式。相同的api会根据调用时promise不同的状态,执行不同的操作。
    也就是说,当我调用then的时候,promise只可能处于 未完成&已完成 的状态其中一种。

    • 如果,我调用then的时候promise未完成,那么promise会把我写入的回调维护起来,等完成后一起调用
    • 如果,我调用then的时候promise已经完成了,那么promise也不会立刻调用我写入的回调。promise会异步执行我写入的回调




promise和不靠谱的回调

不靠谱的回调

如果说回调地狱带来的困扰勉强还可以克服的话,那么不稳定的宿主环境带来的问题,就是无论如何都不能忽视的了。因为我们对回调的行为的无法确定,所以我们失去了对回调的信任。


不被信任的回调,到底会做出些什么诡异操作?

  • 调用回调过早
  • 调用回调过晚
  • 回调次数不确定
  • 未能传递所需的环境和参数
  • 异常丢失

这些问题是怎么出现的?

调用回调过早

异步任务总是有长有短,他可不会等你把回调托付给宿主后再让自己执行完。这样一来异步任务和回调之间就出现了一种竟态,总会有你还没有写入回调,异步任务就执行完的情况。
这样的话宿主就抓不到回调,从而也就不执行回调了


调用回调过晚

当我们对一个异步任务有多个回调的时候,通常我们是没有办法判断这些回调的执行顺序的。我们知道,对于回调来说,每次触发回调,回调函数都需要到事件循环的结尾重新排队的

回调次数不确定(甚至可能是0次)* & *未能传递所需的环境和参数

这两个问题很常见,会不会出现完全取决于宿主的心情。根据定义,正确的回调调用次数应该是1次。但是有些宿主就是这么叛逆

异常丢失

这里的异常是指异步任务在执行的时候出现的异常
异步任务是在宿主环境被调用的,所以抛出的异常也会先体现到宿主环境中。是否要捕获并处理这些异常,需要你自己在回调中定义



Promise的解决方案

调用回调过早

调用过早根源在于回调函数的输入时间点和异步任务执行完成的时间点存在竟态
而在promise中,只存在“未来”才要完成的异步任务,其中的原理上文已经描述得很清楚了


调用回调过晚

在我们显性调用resolve或者reject触发then之后。剩下的回调已经由JavaScript引擎掌握了主动权了,更何况在ES6之后,还有任务队列的概念。promise是在任务队列中执行的 微任务。根本不需要担心调用过晚的问题
而且,then之间是用链状模式连接的,就像这样:

在这里插入图片描述

promise可以保证这一段一定输出ABC

虽然输出A的时候调用了C的then,但是并不影响已经注册的B

但是不同的promise之间的then执行顺序是不确定的,就像这样:

在这里插入图片描述

这段代码的结果是:A B

因为当promise1解析promise3的时候,这个解析的动作是放在promise1的then回调里面的。而我们说过,promise的then回调是要进任务队列的。而在任务队列中,新加入的对promise3的解析动作,就没有promise2的优先级高了。


回调次数不确定

首先要达成一个共识,没有任何东西(甚至JavaScript错误)可以阻止promise向你履行他的承诺。如果你的promise的定义是完整的(即,带有resolve、reject和catch的任务),那么他一定会调用其中的某一个。

  • 一次都不调用
    除开上面说的所有不确定性以外,还是有一种情况会让promise的回调不执行。那就是你输入的异步操作无法结束。那么promise就永远拿不到【未来值】。这时候一般我们会给promise中的异步任务增加一个“定时器”,当promise超时的时候,就视为异步操作无法结束。就像这样:

    在这里插入图片描述

    输出:

    在这里插入图片描述

  • 调用次数过多
    promise只可能通过一次决议,所以不用担心调用次数过多的问题。promise的then回调,有且只会跑一次


未能传递所需的环境和参数

这个问题和上一个问题一起被解决了
而当你有多个【未来值】的时候,promise会把他们组合形成一个数组,一起传给then回调


异常丢失

promise通过在定义异步任务的时候所传入的reject回调来实现对这个问题的处理。
你可以显性的调用reject(就像上面那个例子一样),但是即使你没有那样做,当出现异常的时候,promise也会自动调用reject回调。因为promise拿不到【未来值】
所以除非你没有定义reject,否则promise中就不会出现异常丢失的情况




为什么promise值得被信任

我们使用promise的初衷,是因为我们发现回调不值得被信任。所以我们追求一种新的方式以实现异步回调的效果
但是现在我们发现,就算我们用promise,也没有摆脱回调。promise只是改变了回调的位置和方式

那为什么promise就比回调更值得被信任呢?

要回答这个问题,要先回答我们到底在不信任什么
我们把异步任务托付给宿主之后,就失联了
但是我们用promise创建异步回调的时候,他会监视整个异步任务的执行过程,虽然没办法直接参与。无论异步任务最终有没有被完成,promise都一定会给你一个结果,而不是像回调一样,凭空消失





万分感谢您看完这篇文章,如果您喜欢这篇文章,欢迎点赞、收藏。还可以通过专栏,查看更多与【JavaScript笔记】有关的内容

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1837334.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

在等保2.0框架下,如何进行有效的物联网设备安全培训和意识提升?

在等保2.0框架下,进行有效的物联网设备安全培训和意识提升,可以从以下几个方面入手: 1. 分层次培训内容设计: • 基础知识普及:涵盖物联网的定义、特点及其面临的安全威胁和攻击手段,让员工理解物联网安全…

技巧|手机上看SwanLab实验的两种方法

什么是SwanLab? SwanLab是一个深度学习实验管理与训练可视化工具,由西安电子科技大学创业团队打造,融合了Weights & Biases与Tensorboard的特点,可以记录整个实验的超参数、指标、训练环境、Python版本等,并可视化图表&…

【2024最新华为OD-C/D卷试题汇总】[支持在线评测] A先生的货运计划(200分) - 三语言AC题解(Python/Java/Cpp)

🍭 大家好这里是清隆学长 ,一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-C/D卷的三语言AC题解 💻 ACM银牌🥈| 多次AK大厂笔试 | 编程一对一辅导 👏 感谢大家的订阅➕ 和 喜欢💗 📎在线评测链接 A先生的货运计划(200分) 🌍 评测功能需要 订阅专栏 后私信…

科普童话投稿

《科普童话》杂志是由国家新闻出版总署批准、黑龙江省教育厅主管、黑龙江省语言文字报刊社主办的正规期刊。《科普童话》以培养科学素养与创新探索精神为办刊宗旨,以科学与艺术统一为编辑方针,以科学教育、教育科学作为自己的出发点,致力于对…

揭秘网络盗版游戏产业链,守护游戏安全 | 天堂1私服非法牟利,涉嫌洗黑钱!

近年来,网络盗版游戏现象日益猖獗,尤其是天堂1私服。这些盗版游戏不仅非法牟利,还存在偷税漏税、诱导消费等违法行为。本文将揭示这一产业链的真相,提醒广大游戏玩家保持警惕,并向相关部门举报,共同维护互联…

安卓软件自动运行插件的开发源代码介绍!

随着移动互联网的快速发展,安卓操作系统凭借其开放性和灵活性,成为了众多开发者们的首选平台,在安卓应用的开发中,为了实现各种复杂的功能,插件化技术逐渐受到青睐。 其中,自动运行插件作为一种能够实现应…

MT8766安卓4G核心板_MTK联发科PCBA方案开发

MT8766是联发科四核4G模块方案,安卓一体板。 采用台积电 12 nm FinFET 制程工艺,4*A53架构,Android 9.0操作系统,搭载2.0GHz 的 Arm NEON 引擎。提供了支持最新 OpenOS 及其要求苛刻的应用程序所需的处理能力,专为具有…

可提供实习证明/实习鉴定报告,企业项目试岗实训开营啦

在数字化转型的浪潮中,大数据和人工智能等前沿技术已成为推动经济发展和科技进步的关键动力。当前,全球各行各业都在积极推进数字化转型,不仅为经济增长注入新活力,也对人才市场结构产生了深刻影响,尤其是对数字化人才…

CCS环形低角度光源用于细微凹凸、损伤、刻印字符的成像——LDR2-LA1系列

机器视觉系统中,光源起着重要作用,不同类型的光源应用也不同,选择合适的光源成像效果非常明显。今天我们一起来看看CCS光源——工业用环形低角度光源LDR2-LA1系列,可对被测物体近距离使用。 LDR2-LA1 特点 1、从被测物体的最近距…

数据可视化实验四:Pyecharts数据可视化

目录 一、使用PyEcharts绘制全国肺炎确诊人数分布图 1.1 柱状图 1.1.2 代码实现 1.1.2 绘制结果 1.2 饼状图 1.2.1 代码实现 1.2.2 绘制结果 1.3 使用over lap实现图形叠加 1.3.1 代码实现 1.3.2 绘制结果 1.4 地图绘制-Map 1.4.1 代码实现 1.4.2 绘制结果 1.5 地…

【ai】如何在ollama中随意使用hugging face上的gguf开源模型

【背景】 ollama的pull命令可以直接pull ollama列表中现有的模型,但是ollama可以直接pull的模型大都是英语偏好(llama2有直接可以pull的chinese版本),而hugging face上则有大量多语种训练的模型,如果能直接使用huggin…

如何使用GPT?初学者的指南

ChatGPT是一个非常先进的AI工具,它使用GPT-4架构,能够生成自然的语言回应。它的多功能性和理解复杂指令的能力,使得很多人用它来回答各种问题,就像用Google一样输入关键词。不过,ChatGPT还能做更多事情,下面…

低代码结合自研项目打包发布

nginx配置 #user nobody; worker_processes 1;#error_log logs/error.log; #error_log logs/error.log notice; #error_log logs/error.log info;#pid logs/nginx.pid;events {worker_connections 1024; }http {include mime.types;default_type applica…

成都晨持绪科技:2024年抖音网店做起来难吗

随着抖音平台的日益火爆,越来越多的商家和个人开始关注并尝试开设自己的抖音网店。然而,面对激烈的市场竞争和不断变化的平台规则,许多人都在问:2024年抖音网店做起来难吗? 要回答这个问题,我们首先需要了解抖音网店的…

SSM整合-前后端分离(实现增删改查)

实现增删改查 实现功能03-添加家居信息需求分析/图解思路分析代码实现注意事项和细节 实现功能04-显示家居信息需求分析/图解思路分析代码实现 实现功能05-修改家居信息需求分析/图解思路分析代码实现注意事项和细节 实现功能06-删除家居信息需求分析/图解思路分析代码实现课后…

微信ipad协议8049新版本

首先我们要先了解下ipad协议是什么 ,ipad协议又叫微信协议 是基于微信IPad协议的智能控制系统帮助企业快速连接客户,创造营销氛围,实现自动获客、自动传播、自动转化、智能营销等分布式营销服务。 通过API 实现 个性化微信功能 (例…

和鲸101计划:以神经计算建模培训,助力北大学术人才培养

探索与求知,培养与传承。 让青年人更早地触摸到科学研究的前沿,便能吸引更多人才投身于学科建设。 11月4日,由北京大学信息处理实验室开展进行,北京大学心理与认知科学学院院长吴思教授及课题组成员授课的第二届神经计算建模及编…

Python 自动化测试入门有哪些内容?

自动化测试是软件测试领域中的一个重要技术,它利用脚本和工具来执行测试任务,减少了人工操作的工作量和时间消耗。Python 是一种功能强大且易于学习的编程语言,被广泛应用于自动化测试领域。本文将从0到1讲解如何使用 Python 进行自动化测试&…

全开源版人才招聘系统源码 小程序运营级平台源码 类似58同城招聘、智联招聘平台

在当今数字化时代,人才招聘与平台运营成为了企业发展的重要环节。分享一套功能全面、易于二次开发的人才招聘系统源码小程序运营级平台源码。这些源码基于类似58同城招聘、智联招聘等大型招聘平台的设计理念,旨在为企业提供高效、便捷的人才招聘与平台运…

企业电脑防泄密软件有哪些|电脑防泄密软件“琅琊榜”排名

企业电脑防泄密软件是用于保护企业内部数据安全、防止敏感信息泄露的专业工具。电脑防泄密软件“琅琊榜”都有哪些呢?以下是八款推荐的防泄密软件。 电脑防泄密软件“琅琊榜”: 1.安企神 - 综合性防泄密解决方案,包括上网行为审计、文档加密…