0

0

详细介绍JavaScript中Promise的基本概念及使用方法

WBOY

WBOY

发布时间:2022-07-12 13:38:50

|

1743人浏览过

|

来源于CSDN

转载

本篇文章给大家带来了关于javascript的相关知识,其中主要整理了promise的基本概念及使用方法的相关问题,包括了promise基本概念、使用promise解决回调地狱等等内容,下面一起来看一下,希望对大家有帮助。

详细介绍JavaScript中Promise的基本概念及使用方法

【相关推荐:javascript视频教程web前端

一、前言

异步是为了提高CPU的占用率,让其始终处于忙碌状态。

有些操作(最典型的就是I/O)本身不需要CPU参与,而且非常耗时,如果不使用异步就会形成阻塞状态,CPU空转,页面卡死。

立即学习Java免费学习笔记(深入)”;

在异步环境下发生I/O操作,CPU就把I/O工作扔一边(此时I/O由其他控制器接手,仍然在数据传输),然后处理下一个任务,等I/O操作完成后通知CPU(回调就是一种通知方式)回来干活。

《JavaScript异步与回调》想要表达的核心内容是,异步工作的具体结束时间是不确定的,为了准确的在异步工作完成后进行后继的处理,就需要向异步函数中传入一个回调,从而在完成工作后继续下面的任务。

虽然回调可以非常简单的实现异步,但是却会由于多重嵌套形成回调地狱。避免回调地狱就需要解嵌套,将嵌套编程改为线性编程。

PromiseJavaScript中处理回调地狱最优解法。

二、Promise基本概念

Promise可以翻译为“承诺”,我们可以通过把异步工作封装称一个Promise,也就是做出一个承诺,承诺在异步工作结束后给出明确的信号!

Promise语法:

let promise = new Promise(function(resolve,reject){
    // 异步工作})

通过以上语法,我们就可以把异步工作封装成一个Promise。在创建Promise时传入的函数就是处理异步工作的方法,又被称为executor(执行者)。

resolvereject是由JavaScript自身提供的回调函数,当executor执行完了任务就可以调用:

  • resolve(result)——如果成功完成,并返回结果result
  • reject(error)——如果执行是失败并产生error

executor会在Promise创建完成后立即自动执行,其执行状态会改变Promise内部属性的状态:

  • state——最初是pending,然后在resolve被调用后转为fulfilled,或者在reject被调用时变为rejected
  • result——最初时undefined,然后在resolve(value)被调用后变为value,或者在reject被调用后变为error;

2.1 异步工作的封装

文件模块的fs.readFile就是一个异步函数,我们可以通过在executor中执行文件读取操作,从而实现对异步工作的封装。

以下代码封装了fs.readFile函数,并使用resolve(data)处理成功结果,使用reject(err)处理失败的结果。

代码如下:

let promise = new Promise((resolve, reject) => {
    fs.readFile('1.txt', (err, data) => {
        console.log('读取1.txt')
        if (err) reject(err)
        resolve(data)
    })})

如果我们执行这段代码,就会输出“读取1.txt”字样,证明在创建Promise后立刻就执行了文件读取操作。

Promise内部封装的通常都是异步代码,但是并不是只能封装异步代码。

2.2 Promise执行结果获取

以上Promise案例封装了读取文件操作,当完成创建后就会立即读取文件。如果想要获取Promise执行的结果,就需要使用thencatchfinally三个方法。

then

Promisethen方法可以用来处理Promise执行完成后的工作,它接收两个回调参数,语法如下:

promise.then(function(result),function(error))
  • 第一个回调函数用于处理成功执行后的结果,参数result就是resolve接收的值;
  • 第二个回调函数用于处理失败执行后的结果,参数error就是reject接收的参数;

举例:

let promise = new Promise((resolve, reject) => {
    fs.readFile('1.txt', (err, data) => {
        console.log('读取1.txt')
        if (err) reject(err)
        resolve(data)
    })})promise.then(
    (data) => {
        console.log('成功执行,结果是' + data.toString())
    },
    (err) => {
        console.log('执行失败,错误是' + err.message)
    })

如果文件读取成功执行,会调用第一个函数:

PS E:\Code\Node\demos\03-callback> node .\index.js
读取1.txt
成功执行,结果是1

删掉1.txt,执行失败,就会调用第二个函数:

PS E:\Code\Node\demos\03-callback> node .\index.js
读取1.txt
执行失败,错误是ENOENT: no such file or directory, open 'E:\Code\Node\demos\03-callback\1.txt'

如果我们只关注成功执行的结果,可以只传入一个回调函数:

promise.then((data)=>{
    console.log('成功执行,结果是' + data.toString())})

到这里我们就是实现了一次文件的异步读取操作。

PHP高级开发技巧与范例
PHP高级开发技巧与范例

PHP是一种功能强大的网络程序设计语言,而且易学易用,移植性和可扩展性也都非常优秀,本书将为读者详细介绍PHP编程。 全书分为预备篇、开始篇和加速篇三大部分,共9章。预备篇主要介绍一些学习PHP语言的预备知识以及PHP运行平台的架设;开始篇则较为详细地向读者介绍PKP语言的基本语法和常用函数,以及用PHP如何对MySQL数据库进行操作;加速篇则通过对典型实例的介绍来使读者全面掌握PHP。 本书

下载

catch

如果我们只关注失败的结果,可以把第一个then的回调传nullpromise.then(null,(err)=>{...})

亦或者采用更优雅的方式:promise.catch((err)=>{...})

let promise = new Promise((resolve, reject) => {
    fs.readFile('1.txt', (err, data) => {
        console.log('读取1.txt')
        if (err) reject(err)
        resolve(data)
    })})promise.catch((err)=>{
    console.log(err.message)})

.catch((err)=>{...})then(null,(err)=>{...})作用完全相同。

finally

.finallypromise不论结果如何都会执行的函数,和try...catch...语法中的finally用途一样,都可以处理和结果无关的操作。

例如:

new Promise((resolve,reject)=>{
    //something...}).finally(()=>{console.log('不论结果都要执行')}).then(result=>{...}, err=>{...})
  • finally回调没有参数,不论成功与否都会执行
  • finally会传递promise的结果,所以在finally后仍然可以.then

三、使用Promise解决回调地狱

3.1 回调地狱出现的场景

现在,我们有一个需求:使用fs.readFile()方法顺序读取10个文件,并把十个文件的内容顺序输出。

由于fs.readFile()本身是异步的,我们必须使用回调嵌套的方式,代码如下:

fs.readFile('1.txt', (err, data) => {
    console.log(data.toString()) //1
    fs.readFile('2.txt', (err, data) => {
        console.log(data.toString())
        fs.readFile('3.txt', (err, data) => {
            console.log(data.toString())
            fs.readFile('4.txt', (err, data) => {
                console.log(data.toString())
                fs.readFile('5.txt', (err, data) => {
                    console.log(data.toString())
                    fs.readFile('6.txt', (err, data) => {
                        console.log(data.toString())
                        fs.readFile('7.txt', (err, data) => {
                            console.log(data.toString())
                            fs.readFile('8.txt', (err, data) => {
                                console.log(data.toString())
                                fs.readFile('9.txt', (err, data) => {
                                    console.log(data.toString())
                                    fs.readFile('10.txt', (err, data) => {
                                        console.log(data.toString())
                                        // ==> 地狱之门
                                    })
                                })
                            })
                        })
                    })
                })
            })
        })
    })})

虽然以上代码能够完成任务,但是随着调用嵌套的增加,代码层次变得更深,维护难度也随之增加,尤其是我们使用的是可能包含了很多循环和条件语句的真实代码,而不是例子中简单的 console.log(...)

3.2 不使用回调产生的后果

如果我们不使用回调,直接把fs.readFile()顺序的按照如下代码调用一遍,会发生什么呢?

//注意:这是错误的写法fs.readFile('1.txt', (err, data) => {
    console.log(data.toString())})fs.readFile('2.txt', (err, data) => {
    console.log(data.toString())})fs.readFile('3.txt', (err, data) => {
    console.log(data.toString())})fs.readFile('4.txt', (err, data) => {
    console.log(data.toString())})fs.readFile('5.txt', (err, data) => {
    console.log(data.toString())})fs.readFile('6.txt', (err, data) => {
    console.log(data.toString())})fs.readFile('7.txt', (err, data) => {
    console.log(data.toString())})fs.readFile('8.txt', (err, data) => {
    console.log(data.toString())})fs.readFile('9.txt', (err, data) => {
    console.log(data.toString())})fs.readFile('10.txt', (err, data) => {
    console.log(data.toString())})

以下是我测试的结果(每次执行的结果都是不一样的):

PS E:\Code\Node\demos\03-callback> node .\index.js12346957108

产生这种非顺序结果的原因是异步,并非多线程并行,异步在单线程里就可以实现。

之所以在这里使用这个错误的案例,是为了强调异步的概念,如果不理解为什么会产生这种结果,一定要回头补课了!

3.3 Promise解决方案

使用Promise解决异步顺序文件读取的思路:

  1. 封装一个文件读取promise1,并使用resolve返回结果
  2. 使用promise1.then接收并输出文件读取结果
  3. promise1.then中创建一个新的promise2对象,并返回
  4. 调用新的promise2.then接收并输出读取结果
  5. promise2.then中创建一个新的promise3对象,并返回
  6. 调用新的promise3.then接收并输出读取结果

代码如下:

let promise1 = new Promise((resolve, reject) => {
    fs.readFile('1.txt', (err, data) => {
        if (err) reject(err)
        resolve(data)
    })})let promise2 = promise1.then(
    data => {
        console.log(data.toString())
        return new Promise((resolve, reject) => {
            fs.readFile('2.txt', (err, data) => {
                if (err) reject(err)
                resolve(data)
            })
        })
    })let promise3 = promise2.then(
    data => {
        console.log(data.toString())
        return new Promise((resolve, reject) => {
            fs.readFile('3.txt', (err, data) => {
                if (err) reject(err)
                resolve(data)
            })
        })
    })let promise4 = promise3.then(
    data => {
        console.log(data.toString())
        //.....
    })... ...

这样我们就把原本嵌套的回调地狱写成了线性模式。

但是代码还存在一个问题,虽然代码从管理上变的美丽了,但是大大增加了代码的长度。

3.4 链式编程

以上代码过于冗长,我们可以通过两个步骤,降低代码量:

  • 封装功能重复的代码,完成文件读取和输出工作
  • 省略中间promise的变量创建,将.then链接起来

代码如下:

function myReadFile(path) {
    return new Promise((resolve, reject) => {
        fs.readFile(path, (err, data) => {
            if (err) reject(err)
            console.log(data.toString())
            resolve()
        })
    })}myReadFile('1.txt')
    .then(data => { return myReadFile('2.txt') })
    .then(data => { return myReadFile('3.txt') })
    .then(data => { return myReadFile('4.txt') })
    .then(data => { return myReadFile('5.txt') })
    .then(data => { return myReadFile('6.txt') })
    .then(data => { return myReadFile('7.txt') })
    .then(data => { return myReadFile('8.txt') })
    .then(data => { return myReadFile('9.txt') })
    .then(data => { return myReadFile('10.txt') })

由于myReadFile方法会返回一个新的Promise,我们可以直接执行.then方法,这种编程方式被称为链式编程

代码执行结果如下:

PS E:\Code\Node\demos\03-callback> node .\index.js12345678910

这样就完成了异步且顺序的文件读取操作。

注意:在每一步的.then方法中都必须返回一个新的Promise对象,否则接收到的将是上一个旧的Promise

这是因为每个then方法都会把它的Promise继续向下传递。

【相关推荐:javascript视频教程web前端

相关文章

java速学教程(入门到精通)
java速学教程(入门到精通)

java怎么学习?java怎么入门?java在哪学?java怎么学才快?不用担心,这里为大家提供了java速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
c语言中null和NULL的区别
c语言中null和NULL的区别

c语言中null和NULL的区别是:null是C语言中的一个宏定义,通常用来表示一个空指针,可以用于初始化指针变量,或者在条件语句中判断指针是否为空;NULL是C语言中的一个预定义常量,通常用来表示一个空值,用于表示一个空的指针、空的指针数组或者空的结构体指针。

236

2023.09.22

java中null的用法
java中null的用法

在Java中,null表示一个引用类型的变量不指向任何对象。可以将null赋值给任何引用类型的变量,包括类、接口、数组、字符串等。想了解更多null的相关内容,可以阅读本专题下面的文章。

458

2024.03.01

scripterror怎么解决
scripterror怎么解决

scripterror的解决办法有检查语法、文件路径、检查网络连接、浏览器兼容性、使用try-catch语句、使用开发者工具进行调试、更新浏览器和JavaScript库或寻求专业帮助等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

228

2023.10.18

500error怎么解决
500error怎么解决

500error的解决办法有检查服务器日志、检查代码、检查服务器配置、更新软件版本、重新启动服务、调试代码和寻求帮助等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

297

2023.10.25

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

523

2023.08.10

Python 多线程与异步编程实战
Python 多线程与异步编程实战

本专题系统讲解 Python 多线程与异步编程的核心概念与实战技巧,包括 threading 模块基础、线程同步机制、GIL 原理、asyncio 异步任务管理、协程与事件循环、任务调度与异常处理。通过实战示例,帮助学习者掌握 如何构建高性能、多任务并发的 Python 应用。

186

2025.12.24

java多线程相关教程合集
java多线程相关教程合集

本专题整合了java多线程相关教程,阅读专题下面的文章了解更多详细内容。

15

2026.01.21

C++多线程相关合集
C++多线程相关合集

本专题整合了C++多线程相关教程,阅读专题下面的的文章了解更多详细内容。

15

2026.01.21

C++ 设计模式与软件架构
C++ 设计模式与软件架构

本专题深入讲解 C++ 中的常见设计模式与架构优化,包括单例模式、工厂模式、观察者模式、策略模式、命令模式等,结合实际案例展示如何在 C++ 项目中应用这些模式提升代码可维护性与扩展性。通过案例分析,帮助开发者掌握 如何运用设计模式构建高质量的软件架构,提升系统的灵活性与可扩展性。

7

2026.01.30

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
React 教程
React 教程

共58课时 | 4.3万人学习

TypeScript 教程
TypeScript 教程

共19课时 | 2.5万人学习

Bootstrap 5教程
Bootstrap 5教程

共46课时 | 3.1万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号