JS-29-Promise对象

一、JavaScript的异步操作

在JavaScript的世界中,所有代码都是单线程执行的。

由于这个“缺陷”,导致JavaScript的所有网络操作,浏览器事件,都必须是异步执行。异步执行可以用回调函数实现:

function callback() {
    console.log('Done');
}
console.log('before setTimeout()');
setTimeout(callback, 1000); // 1秒钟后调用callback函数
console.log('after setTimeout()');

观察上述代码执行,在Chrome的控制台输出可以看到:

before setTimeout()
after setTimeout()
(等待1秒后)
Done

可见,异步操作会在将来的某个时间点触发一个函数调用

AJAX就是典型的异步操作。以上一节的代码为例:

request.onreadystatechange = function () {
    if (request.readyState === 4) {
        if (request.status === 200) {
            return success(request.responseText);
        } else {
            return fail(request.status);
        }
    }
}

把回调函数success(request.responseText)fail(request.status)写到一个AJAX操作里很正常,但是不好看,而且不利于代码复用。

有没有更好的写法?比如写成这样:

var ajax = ajaxGet('http://...');
ajax.ifSuccess(success)
    .ifFail(fail);

这种链式写法的好处在于,先统一执行AJAX逻辑,不关心如何处理结果,然后,根据结果是成功还是失败,在将来的某个时候调用success函数或fail函数。

古人云:“君子一诺千金”,这种“承诺将来会执行”的对象在JavaScript中称为Promise对象。

1-1、setTimeout函数

setTimeout 是 JavaScript 中一个非常常用的函数,用于在指定的延迟后执行一个函数或计算一个表达式。它返回一个代表定时器的ID,这个ID可以用来在将来必要的时候取消定时器(使用 clearTimeout 函数)。

语法:

var timerId = setTimeout(func|code, [delay], [arg1], [arg2], ...)
  • func|code:要执行的函数或要计算的表达式。
  • delay:可选参数,表示延迟的毫秒数,即多长时间后执行函数或表达式。默认是 0。
  • arg1, arg2, ...:可选参数,表示传递给函数的额外参数。

示例:

setTimeout(function() {  
    console.log('Hello, world!');  
}, 2000);

在这个例子中,console.log('Hello, world!') 将在 2 秒(2000 毫秒)后执行。

function greet(name) {  
    console.log('Hello, ' + name + '!');  
}  
  
setTimeout(greet, 2000, 'Alice');

在这个例子中,greet 函数将在 2 秒后执行,并且会传递 'Alice' 作为参数。

setTimeout 只会执行一次指定的函数或代码。如果你想要重复执行某个函数或代码,你应该使用 setInterval 或者在函数内部再次调用 setTimeout

1-2、清除定时器 clearTimeout 函数

如果你想要在某个时刻取消定时器,你可以使用 clearTimeout 函数,并传入 setTimeout 返回的定时器ID。

语法:

clearTimeout(timeoutID)

示例1: 

var timerId = setTimeout(function() {  
    console.log('This will not be logged.');  
}, 5000);  
  
// 假设在某个时刻,我们决定取消这个定时器  
clearTimeout(timerId);

在这个例子中,由于我们调用了 clearTimeout,所以 console.log 不会被执行。

示例2:

// 设置一个定时器,将在 2 秒后执行  
var timerId = setTimeout(function() {  
    console.log('Timer executed!');  
}, 2000);  
  
// 在 1 秒后取消定时器  
setTimeout(function() {  
    clearTimeout(timerId);  
    console.log('Timer cleared!');  
}, 1000);

你将在控制台看到 "Timer cleared!" 的输出,而不会看到 "Timer executed!" 的输出。

1-3、链式调用

链式调用,允许我们在单个对象上连续调用多个方法,并且每次方法调用都返回同一个对象,以便可以进一步调用其他方法。

优点:这种模式可以提高代码的可读性和简洁性。

 1-4、Promise对象

Promise有各种开源实现,在ES6中被统一规范,由浏览器直接支持。

在JavaScript中,Promise对象用于处理异步操作,它代表了一个可能现在、将来或永远不可用的值。

Promise对象有三种状态pending(进行中)、fulfilled(已成功)和rejected(已失败)。一旦Promise的状态从pending变为fulfilledrejected,就不会再改变。

创建Promise对象的基本语法如下:

const promise = new Promise((resolve, reject) => {  
  // 异步操作代码  
  if (/* 异步操作成功 */) {  
    resolve(value); // 将Promise的状态设置为fulfilled,并传递一个值  
  } else {  
    reject(error); // 将Promise的状态设置为rejected,并传递一个错误  
  }  
});

 resolvereject都是函数,它们由Promise构造函数传递进来,用于处理异步操作的结果。

示例:

const promise = new Promise((resolve, reject) => {  
  setTimeout(() => {  
    const success = true;  
    if (success) {  
      resolve('异步操作成功!');  
    } else {  
      reject('异步操作失败!');  
    }  
  }, 1000);  
});  
  
promise.then(value => {  
  console.log(value); // 如果Promise状态为fulfilled,则执行这里的代码  
}).catch(error => {  
  console.error(error); // 如果Promise状态为rejected,则执行这里的代码  
});

在JavaScript的Promise构造函数中,resolvereject是两个非常关键的函数参数。它们被用来改变Promise对象的状态,并传递最终的结果或错误给Promise链中的后续处理函数。

1、resolve函数

resolve函数用于将Promise对象的状态从pending(进行中)变为fulfilled(已完成),并传递一个值给后续的.then()处理函数

2、reject函数

reject函数用于将Promise对象的状态从pending变为rejected(已拒绝),并传递一个错误对象给后续的.catch()处理函数

3、Promise链中的错误处理

Promise链中,如果你没有在每个.then()之后立即使用.catch()来处理可能发生的错误。

你可以在链的末尾使用单个.catch()来捕获所有之前的.then()中可能抛出的错误。这是因为,一旦Promise链中的某个环节发生错误,该错误会“冒泡”到链的末尾,除非在某个环节被捕获处理。

promise  
  .then(result => {  
    // 处理结果...  
    // 如果这里发生错误且没有捕获,它会传递到链的末尾的.catch()中  
  })  
  .then(anotherResult => {  
    // 处理另一个结果...  
    // 同样,这里的错误也会“冒泡”到链的末尾  
  })  
  .catch(error => {  
    // 处理链中任何环节的错误  
    console.error(error);  
  });

我们先看一个最简单的Promise例子:

生成一个0-2之间的随机数,如果小于1,则等待一段时间后返回成功,否则返回失败:

    <div id="test-promise-log" style="border: solid 1px #ccc; padding: 1em; margin: 15px 0;">
        <p>Log:</p>
    </div>

    <script>
        "use script"
        
        function testLian(){

            // 清除log:
            var logging = document.getElementById('test-promise-log');
            while (logging.children.length > 1) {
                logging.removeChild(logging.children[logging.children.length - 1]);
            }

            // 输出log到页面:
            function log(s) {
                var p = document.createElement('p');
                p.innerHTML = s;
                logging.appendChild(p);
            }

            new Promise(function (resolve, reject) {
                log('start new Promise...');
                var timeOut = Math.random() * 2;
                log('set timeout to: ' + timeOut + ' seconds.');
                setTimeout(function () {
                    if (timeOut < 1) {
                        log('call resolve()...');
                        resolve('200 OK');
                    }
                    else {
                        log('call reject()...');
                        reject('timeout in ' + timeOut + ' seconds.');
                    }
                }, timeOut * 1000);
            }).then(function (r) {
                log('Done: ' + r);
            }).catch(function (reason) {
                log('Failed: ' + reason);
            });
        }
        
    </script>

可见Promise最大的好处是在异步执行的流程中,把执行代码处理结果的代码清晰地分离了:

4、Promise处理若干异步任务

Promise还可以做更多的事情,比如,有若干个异步任务,需要先做任务1,如果成功后再做任务2,任何任务失败则不再继续并执行错误处理函数。

串行执行这样的异步任务,不用Promise需要写一层一层的嵌套代码。有了Promise,我们只需要简单地写:

job1.then(job2).then(job3).catch(handleError);

其中,job1job2job3都是Promise对象。

下面的例子演示了如何串行执行一系列需要异步计算获得结果的任务:

// 0.5秒后返回input*input的计算结果:
function multiply(input) {
    return new Promise(function (resolve, reject) {
        log('calculating ' + input + ' x ' + input + '...');
        setTimeout(resolve, 500, input * input);
    });
}

// 0.5秒后返回input+input的计算结果:
function add(input) {
    return new Promise(function (resolve, reject) {
        log('calculating ' + input + ' + ' + input + '...');
        setTimeout(resolve, 500, input + input);
    });
}

var p = new Promise(function (resolve, reject) {
    log('start new Promise...');
    resolve(123);
});

p.then(multiply)
 .then(add)
 .then(multiply)
 .then(add)
 .then(function (result) {
    log('Got value: ' + result);
});

setTimeout可以看成一个模拟网络等异步执行的函数。

1-5、AJAX异步执行函数转换为Promise对象

现在,我们把上一节的AJAX异步执行函数转换为Promise对象,看看用Promise如何简化异步处理:

'use strict';

// ajax函数将返回Promise对象:
function ajax(method, url, data) {
    var request = new XMLHttpRequest();
    return new Promise(function (resolve, reject) {
        request.onreadystatechange = function () {
            if (request.readyState === 4) {
                if (request.status === 200) {
                    resolve(request.responseText);
                } else {
                    reject(request.status);
                }
            }
        };
        request.open(method, url);
        request.send(data);
    });
}

var log = document.getElementById('test-promise-ajax-result');
var p = ajax('GET', '/api/categories');
p.then(function (text) { // 如果AJAX成功,获得响应内容
    log.innerText = text;
}).catch(function (status) { // 如果AJAX失败,获得响应代码
    log.innerText = 'ERROR: ' + status;
});

1-6、Promise.all()

除了串行执行若干异步任务外,Promise还可以并行执行异步任务。

试想一个页面聊天系统,我们需要从两个不同的URL分别获得用户的个人信息和好友列表,这两个任务是可以并行执行的,用Promise.all()实现如下:

        "use script"

        var p1 = new Promise(function (resolve, reject) {
            setTimeout(resolve, 500, 'A1');
        });
        var p2 = new Promise(function (resolve, reject) {
            setTimeout(resolve, 600, 'A2');
        });
        // 同时执行p1和p2,并在它们都完成后执行then:
        Promise.all([p1, p2]).then(function (results) {
            // 获得一个Array: ['A1', 'A2']
            console.log(results);
        });

1-7、Promise.race()

有些时候,多个异步任务是为了容错

比如,同时向两个URL读取用户的个人信息,只需要获得先返回的结果即可。这种情况下,用Promise.race()实现:

var p1 = new Promise(function (resolve, reject) {
    setTimeout(resolve, 500, 'A1');
});
var p2 = new Promise(function (resolve, reject) {
    setTimeout(resolve, 600, 'A2');
});
Promise.race([p1, p2]).then(function (result) {
    console.log(result); // 'A1'
});

由于p1执行较快,Promise的then()将获得结果'A1'p2仍在继续执行,但执行结果将被丢弃

如果我们组合使用Promise,就可以把很多异步任务以并行和串行的方式组合起来执行。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【系统分析师】系统安全分析与设计

文章目录 1、安全基础技术1.1 密码相关1.1.1对称加密1.1.2非对称加密1.1.3信息摘要1.1.4数字签名1.1.5数字信封 1.2 PKI公钥体系 2、信息系统安全2.1 保障层次2.2 网络安全2.2.1WIFI2.2.2 网络威胁与攻击2.2.3 安全保护等级 2.3计算机病毒与木马2.4安全防范体系 1、安全基础技术…

Composer是什么?

Composer是PHP的一个依赖管理工具&#xff0c;它允许开发者声明项目所依赖的代码库&#xff0c;并在项目中自动安装这些依赖。它使用composer.json文件来定义项目的依赖关系&#xff0c;并使用composer.lock文件来锁定依赖的版本&#xff0c;以确保项目的稳定性和可重复性。 Co…

相机系列——透视投影:针孔相机模型

作者&#xff1a;木一 引言 上文我们提到&#xff0c;三维相机是对真实世界成像的模拟&#xff0c;为了让三维物体在计算机屏幕上呈现出来的图像符合人眼观察效果&#xff0c;通常采用透视投影方式模拟相机成像&#xff0c;为了简化计算&#xff0c;可以用针孔相机模型来描述…

STL —— priority_queue

博主首页&#xff1a; 有趣的中国人 专栏首页&#xff1a; C专栏 本篇文章主要讲解 priority_queue 的相关内容 目录 1. 优先级队列简介 基本操作 2. 模拟实现 2.1 入队操作 2.2 出队操作 2.3 访问队列顶部元素 2.4 判断优先队列是否为空 2.5 获取优先队列的大小 …

2024年管理、经济发展与商务分析国际会议(ICMEDBA2024)

2024年管理、经济发展与商务分析国际会议&#xff08;ICMEDBA2024&#xff09; 会议简介 2024年管理、经济发展和商业分析国际会议&#xff08;ICMEDBA2024&#xff09;将在中国昆明举行。会议聚焦管理、经济发展和商业分析研究领域&#xff0c;旨在为相关领域的专家、学者、…

保障通信安全的端到端加密技术

随着互联网技术的飞速发展&#xff0c;人们的通信方式也变得日益多样化和便捷化。然而&#xff0c;通信的便捷性背后也隐藏着信息安全的风险。在这样的背景下&#xff0c;端到端加密技术应运而生&#xff0c;成为了保障通信安全的重要手段。本文将对端到端加密技术进行详细介绍…

RocketMQ 02 功能大纲介绍

RocketMQ 02 主流的MQ有很多&#xff0c;比如ActiveMQ、RabbitMQ、RocketMQ、Kafka、ZeroMQ等。 之前阿里巴巴也是使用ActiveMQ&#xff0c;随着业务发展&#xff0c;ActiveMQ IO 模块出现瓶颈&#xff0c;后来阿里巴巴 通过一系列优化但是还是不能很好的解决&#xff0c;之后…

CodeMaid:Visual Studio代码自动整理插件!

推荐一款Visual Studio的扩展插件&#xff0c;可以帮助开发者更高效地管理和维护代码。 01 插件简介 CodeMaid是一款Visual Studio的扩展插件&#xff0c;其主要功能包括代码整理、代码格式化、自动注释、快速导航等&#xff0c;这些功能都可以提高开发者的编程效率和代码质量…

性能测试-数据库优化二(SQL的优化、数据库拆表、分表分区,读写分离、redis、数据库监控)

数据库优化 explain select 重点&#xff1a; type类型&#xff0c;rows行数&#xff0c;extra SQL的优化 在写on语句时&#xff0c;将数据量小的表放左边&#xff0c;大表写右边where后面的条件尽可能用索引字段&#xff0c;复合索引时&#xff0c;最好按复合索引顺序写wh…

5V_6A/4A高性能低EMI同步降压整流器内置8 mΩ NMOS, 31 mΩ PMOS

概述 PCD1500 是一个非常小、高效、低噪音同步 6A 降压直流/直流变换器&#xff0c;从 2.5V 到 5.5V 的输入电源运行。该变换器使用固定开关频率&#xff0c;在 1MHz 到 10MHz 时进行峰值电流模式控制&#xff0c;最小开关时间低至 22ns&#xff0c;通过较小的外部组件实现快速…

第 6 章 URDF、Gazebo与Rviz综合应用(自学二刷笔记)

重要参考&#xff1a; 课程链接:https://www.bilibili.com/video/BV1Ci4y1L7ZZ 讲义链接:Introduction Autolabor-ROS机器人入门课程《ROS理论与实践》零基础教程 6.7.4 kinect信息仿真以及显示 通过 Gazebo 模拟kinect摄像头&#xff0c;并在 Rviz 中显示kinect摄像头数据…

【Linux系统编程】第三弹---基本指令(一)

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】【Linux系统编程】 目录 1、touch指令 2、mkdir指令 3、ls 指令 4、pwd命令 3、cd 指令 6、rmdir指令 && rm 指令 7、man指令 7、cp指令 …

南京观海微电子---外电路和内电路的区别和联系、应用和优缺点

什么是外电路和内电路 首先&#xff0c;我们要明白什么是电路。电路是指由导体或其他元件连接起来的闭合路径&#xff0c;能够让电流通过。我们平时见到的灯泡、手机、电脑等都是由不同的电路组成的。 电路一点通 “电路一点通”聚电路技术资源、电子电路、高品质电路图、电路…

【计算机毕业设计】4S店车辆管理系统——后附源码

&#x1f389;**欢迎来到我的技术世界&#xff01;**&#x1f389; &#x1f4d8; 博主小档案&#xff1a; 一名来自世界500强的资深程序媛&#xff0c;毕业于国内知名985高校。 &#x1f527; 技术专长&#xff1a; 在深度学习任务中展现出卓越的能力&#xff0c;包括但不限于…

vue+springboot实现聊天功能

前言 在我的项目中&#xff0c;突然有种想法&#xff0c;想实现聊天功能&#xff0c;历经一段时间终于做出来了&#xff1b;那么接下来会讲解如何实现&#xff0c;这篇文章只会实现最基础的逻辑&#xff0c;实时获取对方聊天记录&#xff0c;话不多说&#xff0c;我们就开始吧…

脚本开发与自动化运维

shell脚本开发 grep搜索工具 参数&#xff1a; -A<显示行数>&#xff1a;-A NUM, --after-context NUM&#xff0c;除了显示符合范本样式的那一行之 外&#xff0c;并显示该行之后的内容。 -B<显示行数>&#xff1a;--before-context NUM&#xff0c;除了显示…

nmap、john、tcpdump

Kali是基于Debian的Linux发行版&#xff0c;Kali Linux包含上百个安全相关工具&#xff0c;如渗透测试、安全检测、密码安全、反向工程等。 扫描&#xff1a;获取一些公开、非公开信息为目的&#xff1b;检查潜在的风险、查找可攻击的目标、收集设备/主机/系统/软件信息、发现可…

开源模型应用落地-chatglm3-6b-批量推理-入门篇(四)

一、前言 刚开始接触AI时&#xff0c;您可能会感到困惑&#xff0c;因为面对众多开源模型的选择&#xff0c;不知道应该选择哪个模型&#xff0c;也不知道如何调用最基本的模型。但是不用担心&#xff0c;我将陪伴您一起逐步入门&#xff0c;解决这些问题。 在信息时代&#xf…

从AdTech转战Martech,驰骛科技的PaaS之路

中国最早的Adtech公司之一&#xff0c;在被全资收购后&#xff0c;其创始团队又创立了一家Martech公司。赛道的变更也从侧面反映出中国营销技术市场的发展轨迹。 驰骛科技创始团队来自易传媒核心团队&#xff0c;驰骛科技创始人程华奕是易传媒创始人兼CTO&#xff0c;是中国最早…

修改taro-ui-vue3的tabs组件源码增加数字标签

需求&#xff1a;taro-ui-vue3的tabs组件上增加数字标记 步骤一&#xff1a;node_modules文件夹下找到taro-ui-vue3/lib/tabs/index.js 把173行的这一段替换成下面这段&#xff0c;然后写上样式 default: () > item.number ? [h(View, {class: at-tabs__item_in}, {defau…