吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 869|回复: 6
收起左侧

[学习记录] 【前端学习】-ES6的异步-promise和async/await

[复制链接]
cheekhan 发表于 2023-4-8 11:25
本帖最后由 cheekhan 于 2023-4-8 14:07 编辑

一、promise
1、Promise
promise 对象,是es6中的一种异步编程解决方案。
promise 是一个对象,从其中可以获取异步操作的消息,可以说更像是一个容器,保存着未来才会结束的事件(也就是一个异步的操作)。
promise 对象只有三种状态:进行中、结束、失败。那么它运行时,只能是 从进行中到失败,或者是从进行中到成功。使用promise对象,主要是通过同步的表达形式,来运行异步代码。
2、Promise 的使用
  • 创建promise 对象
  • 使用then 方法
  • 使用 catch 方法
  • 使用 finally 方法


一个简单的promise 对象,构造函数的参数,是一个待执行的函数,函数需要被传入两个回调函数,分别在当前函数体执行成功或失败后,有选择性的执行两个传入的回调函数:
[JavaScript] 纯文本查看 复制代码
let request = new Promise(function(success,fail){[/size]
[size=3]    // code ,函数初始化时需要执行的操作,多数是一个异步操作,例如网络请求操作[/size]
[size=3]    xhr.send()[/size]
[size=3]    ...[/size]
[size=3]    // 然后在异步操作有结果后,选择性的执行成功,或者失败的回调[/size]
[size=3]    xhr.on请求成功事件 = function(){ // 伪代码[/size]
[size=3]        success() ; // 执行成功的回调函数[/size]
[size=3]    }[/size]
[size=3]    xhr.on请求失败事件 = function(){ // 伪代码[/size]
[size=3]        success() ; // 执行失败的回调函数[/size]
[size=3]    }[/size]
[size=3]}))

在 promise 对象实例化的时候,就会去执行需要执行的异步操作,如果需要在异步有结果后,再去执行对应的回调函数,还需要调用 promise 自身的 then 函数:
[JavaScript] 纯文本查看 复制代码
request.then(function(){},function(){});[/size]
[size=3]// then 中传入两个参数[/size]
[size=3]// 如果异步结果是成功的,根据其内的逻辑,应该执行 then 中传入的第一个函数[/size]
[size=3]// 如果异步结果是 失败的,根据期内的逻辑,应该执行 then 中传入的 第二个函数

当异步执行结果失败时,promise 对象调用 then 方法是,第一个参数传入的是null 或者 undefined,所以只会执行 then 函数中的第二个参数。这个过程可以使用 promise 对象的 catch 方法来代替:
[JavaScript] 纯文本查看 复制代码
request.then(function(){[/size]
[size=3]    // 异步成功成功的回调函数[/size]
[size=3]}).catch(function(){[/size]
[size=3]    // 异步失败的回调函数[/size]
[size=3]})

在实际中,还可以再 then 或者 catch 函数中,再次返回一个 promise 对象,then 或者catch 会将回调函数生成的promise 对象,再次返回出来,所以可以使用调用链的方式,一直`then().then().then()`。
finally 方法,用来进行收尾的工作,也就是不管是 promise的状态是成功,还是失败,当执行完回调函数后,都会去寻找 finally 中寻找最后的回调函数来执行:
[JavaScript] 纯文本查看 复制代码
request.finally(function(){[/size]
[size=3]    // 最后 , 且一定会执行的代码[/size]
[size=3]})

3、Promise.all 和Promise.race
如果有一个同步任务,需要等待多个异步任务都执行完毕,才能执行,根据前面已知的方法来实现的话,依然会造成代码难以阅读和维护,所以,如果是需要等待多个异步任务的操作结果,使用`Promise.all` 方法。
`Promise.all` 方法, 传入一个列表,该列表的元素,都是promise 对象。然后promise.all方法,会捕获执行列表中所有的执行结果,再包装成一个新的Promise 对象。
[JavaScript] 纯文本查看 复制代码
let imgs = [1, 2, 3, 4, 5, 6];
let p_list = []; // 子任务列表,每个元素通过循环,变为一个 promise 对象
for (let key of imgs) {
    p_list.push(
        new Promise(function (suc, fail) {
            setTimeout(function () {
                console.log('异步操作:',key);
                suc(key);  // 子 promise 的返回值
            }, 3000)
        }))
}
// Promise.all 执行 子任务列表,返回一个新的promise 对象,所以 Promise.all 后面可以使用 then 方法
// 新的 promise 对象的返回值,是每个子Promise 返回值,组成的列表,即:r = 一个列表,
Promise.all(p_list)
    .then(r=>{
    console.log('all end : ',r);
})

// 最后打印出来,r 的值:[1, 2, 3, 4, 5, 6]


在Promise.all 中,返回值的顺序,是执行时,子任务列表的顺序,也就是说,等所有的子Promise 都执行完毕,如果没有失败,则返回子结果的列表,如果有失败,则在catch 中返回失败的那一个。
而相似的方法,Promise.race(),原理相似,但是返回的是执行最快的结果,其他的子任务,不进行等待。
二、generator
1、generator
generator 值得是一个生成器,也就是一个可迭代对象。其内封存了很多状态,这些状态不是一下就产生的,而是在需要生成的时候,再去调用生成器。所以,遍历生成器,可以获取到其内的每一个状态。定义一个生成器,使用function 后面加一个 * 来定义:
[JavaScript] 纯文本查看 复制代码
// function + * 来定义一个生成器
// * 的位置,可以在function 和 函数名之间的任意位置
function* generators(){
    yield 1;
    yield 2;
}



2、yield 表达式 和 next 方法

yield 表达式,用来生成一个状态。对 一个生成器 产生的作用,类似于一个return,区别是 yield 不会结束函数的执行。yield 会在函数内部形成一个一个的断点,第一次执行函数时,就返回一个生成器。当生成器再次调用时(通过next 方法,这个方法是生成器的原型方法),是从函数顶部开始执行,执行到 yield 表达式时,返回yield 表达式的结果,并且在生成器内部打一个断点,标识当前执行结束的位置。再次调用生成器时(依然是通过 next 来调用),则从上次断点的位置开始继续向下执行,直到遇到一个 yield 表达式,或者 return,则将表达式的值返回,然后再次打一个断点。
[JavaScript] 纯文本查看 复制代码
// a 是直接调用生成器函数,生成的生成器对象
let a = generators();

// 调用生成器对象, 获取第一次状态
// state_1 = {value:1,done:false}
// value 是返回的生成器值,done 代表生成器是否迭代完毕
let state_1 = a.next() ; 

// 可以一直调用 生成器 的 next 方法,直到生成器迭代完毕
let s = a.next();
...
let s = a.next();
// 如果生成器迭代完毕,再次调用next方法时,获取的返回值:{value: undefined, done: true}

yield 和 return 的区别 : 用来返回生成器的一个状态,但不会终结生成器的执行。而return 会终止生成器函数。return之后,再次调用生成器的next 方法, 获取到的结果永远是迭代完成:`{value: undefined, done: true}` 。所以,yield 不能放在 return 之后。next() 函数,用来执行生成器,从而获取生成器的状态。在不传参数的情况下,next 函数调用时,将上一次 生成器的返回值,当做参数,传入函数体内部 ,如果是next 函数接收一个参数,用来在调用生成器时,将传入的参数,当做上一个生成器yield 的执行结果,替换原本产生的结果:
[JavaScript] 纯文本查看 复制代码
function * gg (x){
    let y = yield x+1;
    yield y;
}
let g = gg(1);
g.next(); // g: y 未定义,因为未赋值,就返回了 , yield 的返回值 = 2
g.next(3); // g : y = 3,next 传入的参数,代替上一次yield 执行结果,继续操作,给y赋值,所以y = 3


三、async / await
async 是对generator的再一次语法糖封装,帮我们实现了生成器的调用,使语句更贴近同步代码的表达方式。使用 async 标识的函数,会返回promise 对象,所以 该函数内部,可以添加任何的异步操作代码。可以将 async 函数,看做是多个异步操作,封装的 promise 对象,而await 表达式,就是then的语法糖。
[JavaScript] 纯文本查看 复制代码
// promise 定义的异步操作
var p = new Promise(function(suc){
    setTimeout(function(){ // setTimeout 模仿一个异步操作
        suc(123)
    },3000)
})
// then 执行回调函数,也就是p实例化时,传入的suc 函数,then 回调函数的参数,作为 p 对象的返回值
p.then(function(num){console.log('end:',num)})  


// 整改成 async 方式
// 1. 先定义异步操作,异步操作有个返回值,作为回调函数的参数
function asyncFunction(){
    setTimeout(function(){
        return 123
    },3000)
}
async function main(){
    let num = await asyncFunction() ; // 返回 123
    nsole.log('end:',num); // 上文中,then 中的执行代码
}

如果想使用 await 来执行一个异步操作,那么其调用函数,必须使用 async 来声明。await 能返回一个 promise 对象,也能返回一个值。如果await 返回的是 promise 对象,那么还可以继续给 await 的返回值使用 then 函数。

免费评分

参与人数 2吾爱币 +2 热心值 +2 收起 理由
blindcat + 1 + 1 用心讨论,共获提升!
SmallCode + 1 + 1 我很赞同!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

SmallCode 发表于 2023-4-8 13:31
排版有点难受
tianwaiyoulong 发表于 2023-4-8 13:44
 楼主| cheekhan 发表于 2023-4-8 13:58
抱歉抱歉,第一次用这玩意,文章是以前写的,我直接复制过来了
 楼主| cheekhan 发表于 2023-4-8 15:17
blindcat 发表于 2023-4-8 15:11
感谢分享,就是排版看起来太乱了

是是是,吸取教训
wikiyc 发表于 2023-4-8 19:24
学习了,就有排版看的有点累。
LXS 发表于 2023-4-8 23:59
黑马的课程?
 楼主| cheekhan 发表于 2023-4-10 10:53
LXS 发表于 2023-4-8 23:59
黑马的课程?

没有,我自己写的啊,当时组内好多人不懂,非得说async是同步, 所以出了个文档说明一下
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-25 01:06

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表