0

0

弄懂promise、async、await

hzc

hzc

发布时间:2020-07-04 09:50:02

|

2012人浏览过

|

来源于简书

转载

上一篇呢,主要是聊了聊同步、异步,他们各自引申出来的‘执行栈’、‘消息队列’,以及‘宏任务’、‘微任务’,如果大家对这几个概念不太了解,可以去这个链接:

https://www.jianshu.com/p/61e7844e68d8

宏任务与微任务都是异步的,其中包括ajax请求、计时器等等,我们初步的了解一下promise,知道他是解决异步的一种方式,那么我们常用的一共有哪几种方法呢?

因为涉及的知识点比较多,这篇文章主要是讲一下,回调函数和promise:

一、回调函数

先上代码:
function f2() {
    console.log('2222')
}
function f1(callback){
    console.log('111')
  setTimeout(function () {
    callback(); 
  }, 5000);
  console.log('3333')
}
f1(f2);

先看下打印值是:
111
3333
五秒后2222

相当于主线程执行完了,会通过回调函数去调用f2函数,这个没什么毛病。但是看下下面的例子:

现在我们读取一个文件,fileReader就是一个异步请求

// 这个异步请求就是通过回调函数的方式获取的

var reader = new FileReader()
var file = input.files[0]
reader.readAsText(file, 'utf-8',function(err, data){
    if(err){
        console.log(err)
    } else {
        console.log(data)
    }
})

现在看起来也很不错,但是如果文件上传出错了,我们还要在回调里面做判断,要是我们读取完这个文件接着要读取多个文件呢?是不是应该这么写:

读取完文件1之后再接着读取文件2、3

var reader = new FileReader()
var file = input.files[0]
reader.readAsText(file1, 'utf-8',function(err1, data1){
    if(err1){
        console.log(err1)
    } else {
        console.log(data1)
    }
    reader.readAsText(file2, 'utf-8',function(err2, data2){
        if(err2){
            console.log(err2)
        } else {
            console.log(data2)
        }
        reader.readAsText(file3, 'utf-8',function(err3, data3){
            if(err3){
                console.log(err3)
            } else {
                console.log(data3)
            }
        })
    })
})

这么写可以实现需求,但是这个代码的可读性就比较差,看起来就不那么优雅,也就是我们常说的‘回调地狱’。那么怎么破解这种嵌套式的回调呢?ES6为我们提供了promise:

二、promise

首先我们从字面意思上理解一下什么是promise?promise可以翻译成承诺、保证,这个地方你可以理解为:

女朋友让我干了一件事,虽然还没干完,但是我保证这件事会有一个结果给你,成功(fulfiled)或者失败(rejected),还有一个等待状态(pending)。

还是先上例子

let promise = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(2000) // 成功以后这个resolve会把成功的结果捕捉到
        // reject(2000) // 失败以后这个reject会把失败的结果捕捉到
    }, 1000)
    console.log(1111)
})

promise.then(res => {
    console.log(res) // then里面第一个参数就能拿到捕捉到的成功结果
}, err =>{
    console.log(res)// then里面第二个参数就能拿到捕捉到的失败结果
})

打印结果:

1111
2000(一秒以后)

1、then链式操作

Promise对象的then方法返回一个新的Promise对象,因此可以通过链式调用then方法。

then方法接收两个函数作为参数,第一个参数是Promise执行成功时的回调,第二个参数是Promise执行失败时的回调,这个上面的例子说的很明白了,第二个参数捕捉的就是失败的回调。

两个函数只会有一个被调用,这句话怎么理解呢?
女朋友让你去做西红柿鸡蛋汤,你要么就去做,要么就不做,叫外卖,肯定没有第三种选择。

函数的返回值将被用作创建then返回的Promise对象。这句话应该怎么理解呢?还是上例子:

let promise = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(2000)
    }, 1000)
    console.log(1111)
})
promise.then(res => {
    console.log(res) // 这个地方会打印捕捉到的2000
    return res + 1000 // 这个函数的返回值,返回的就是这个promise对象捕捉到的成功的值
}).then(res => {
    console.log(res) //这个地方打印的就是上一个promise对象return的值
})

所以打印顺序应该是:

1111
2000
3000

刚才我们看到了then接受两个参数,一个是成功的回调、一个是失败的回调,看起来好像也不是那么优雅,promise里除了then还提供了catch方法:

2、catch捕捉操作

这个catch就是专门捕捉错误的回调的,还是先看例子:

Replit Agent
Replit Agent

Replit最新推出的AI编程工具,可以帮助用户从零开始自动构建应用程序。

下载
let promise = new Promise((resolve, reject) => {
    setTimeout(() => {
        reject(2000) // 失败以后这个reject会把失败的结果捕捉到
    }, 1000)
    console.log(1111)
})
promise.catch(res => {
    console.log(res) // catch里面就能拿到捕捉到的失败结果
})

打印结果:

1111
2000(一秒以后)

除了then和catch之外,promise还有两个语法,all和race,我们也简单用一下:

3、all

现在我们有这么一个需求,一共有三个接口A、B、C,必须三个接口都成功以后,才能发起第四个请求,怎么实现呢?

链式调用

let getInfoA = new Promise((resolve, reject) => {
    console.log('小A开始执行了')
    resolve()
}).then(res => {
    let getInfoB = new Promise((resolve, reject) => {
        console.log('小B开始执行了')
        resolve()
    }).then(res => {
        let getInfoC = new Promise((resolve, reject) => {
            console.log('小C开始执行了')
            resolve()
        }).then(res => {
            console.log('全都执行完了!')
        })
    })
})

一层套一层的,好像不是那么优雅

all

let getInfoA = new Promise((resolve, reject) => {
    console.log('小A开始执行了')
    resolve()
})
let getInfoB = new Promise((resolve, reject) => {
    console.log('小B开始执行了')
    resolve()
})
let getInfoC = new Promise((resolve, reject) => {
    console.log('小C开始执行了')
    resolve()
})
Promise.all([getInfoA, getInfoB, getInfoC]).then(res => {
   console.log('全都执行完了!')
})

接收一个Promise对象组成的数组作为参数,当这个数组所有的Promise对象状态都变成resolved或者rejected的时候,它才会去调用then方法。非常完美,非常优雅。

4、race

现在又有一个需求,同样是接口A、B、C,只要有一个响应了,我就可以调接口D,那么怎么实现呢?

1、传统方式

let getInfoA = new Promise((resolve, reject) => {
    console.log('小A开始执行了')
    setTimeout((err => {
        resolve('小A最快')
    }),1000)
}).then(res =>{
    console.log(res)
})
let getInfoB = new Promise((resolve, reject) => {
    console.log('小B开始执行了')
    setTimeout((err => {
        resolve('小B最快')
    }),1001)
}).then(res =>{
    console.log(res)
})
let getInfoC = new Promise((resolve, reject) => {
    console.log('小C开始执行了')
    setTimeout((err => {
        resolve('小C最快')
    }),1002)
}).then(res =>{
    console.log(res)
})

打印结果

小A开始执行了
小B开始执行了
小C开始执行了
小A最快

这个方法得写三遍,好像也不是那么优雅,一起来看下race应该怎么写?

2、race

let getInfoA = new Promise((resolve, reject) => {
    console.log('小A开始执行了')
    setTimeout((err => {
        resolve('小A最快')
    }),1000)
})
let getInfoB = new Promise((resolve, reject) => {
    console.log('小B开始执行了')
    setTimeout((err => {
        resolve('小B最快')
    }),1001)
})
let getInfoC = new Promise((resolve, reject) => {
    console.log('小C开始执行了')
    setTimeout((err => {
        resolve('小C最快')
    }),1002)
})
Promise.race([getInfoA, getInfoB, getInfoC]).then(res => {
    console.log(res)
})

打印结果

小A开始执行了
小B开始执行了
小C开始执行了
小A最快

与Promise.all相似的是,Promise.race都是以一个Promise对象组成的数组作为参数,不同的是,只要当数组中的其中一个Promsie状态变成resolved或者rejected时,就可以调用.then方法了。

promise是ES6用来解决异步的一个方法,现在用的已经比较广泛了,像我们经常用的axios,他就是用promise封装的,用起来非常方便。

除了promise,ES6还为我们提供了终极大招async、await,因为这两个知识块比较大,所以我们准备下一篇文章讲。

个人的微信公众号:小Jerry有话说,平时会发一些技术文章和读书笔记,欢迎交流。

推荐教程:《JS教程

相关专题

更多
ajax教程
ajax教程

php中文网为大家带来ajax教程合集,Ajax是一种用于创建快速动态网页的技术。通过在后台与服务器进行少量数据交换,Ajax可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。php中文网还为大家带来ajax的相关下载资源、相关课程以及相关文章等内容,供大家免费下载使用。

154

2023.06.14

ajax中文乱码解决方法
ajax中文乱码解决方法

ajax中文乱码解决方法有设置请求头部的字符编码、在服务器端设置响应头部的字符编码和使用encodeURIComponent对中文进行编码。本专题为大家提供ajax中文乱码相关的文章、下载、课程内容,供大家免费下载体验。

159

2023.08.31

ajax传递中文乱码怎么办
ajax传递中文乱码怎么办

ajax传递中文乱码的解决办法:1、设置统一的编码方式;2、服务器端编码;3、客户端解码;4、设置HTTP响应头;5、使用JSON格式。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

110

2023.11.15

ajax网站有哪些
ajax网站有哪些

使用ajax的网站有谷歌、维基百科、脸书、纽约时报、亚马逊、stackoverflow、twitter、hacker news、shopify和basecamp等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

228

2024.09.24

es6新特性
es6新特性

es6新特性有:1、块级作用域变量;2、箭头函数;3、模板字符串;4、解构赋值;5、默认参数;6、 扩展运算符;7、 类和继承;8、Promise。本专题为大家提供es6新特性的相关的文章、下载、课程内容,供大家免费下载体验。

97

2023.07.17

es6新特性有哪些
es6新特性有哪些

es6的新特性有:1、块级作用域;2、箭头函数;3、解构赋值;4、默认参数;5、扩展运算符;6、模板字符串;7、类和模块;8、迭代器和生成器;9、Promise对象;10、模块化导入和导出等等。本专题为大家提供es6新特性的相关的文章、下载、课程内容,供大家免费下载体验。

187

2023.08.04

JavaScript ES6新特性
JavaScript ES6新特性

ES6是JavaScript的根本性升级,引入let/const实现块级作用域、箭头函数解决this绑定问题、解构赋值与模板字符串简化数据处理、对象简写与模块化提升代码可读性与组织性。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

219

2025.12.24

硬盘接口类型介绍
硬盘接口类型介绍

硬盘接口类型有IDE、SATA、SCSI、Fibre Channel、USB、eSATA、mSATA、PCIe等等。详细介绍:1、IDE接口是一种并行接口,主要用于连接硬盘和光驱等设备,它主要有两种类型:ATA和ATAPI,IDE接口已经逐渐被SATA接口;2、SATA接口是一种串行接口,相较于IDE接口,它具有更高的传输速度、更低的功耗和更小的体积;3、SCSI接口等等。

1023

2023.10.19

高德地图升级方法汇总
高德地图升级方法汇总

本专题整合了高德地图升级相关教程,阅读专题下面的文章了解更多详细内容。

43

2026.01.16

热门下载

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

精品课程

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

共58课时 | 3.7万人学习

TypeScript 教程
TypeScript 教程

共19课时 | 2.3万人学习

Bootstrap 5教程
Bootstrap 5教程

共46课时 | 2.9万人学习

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

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