JS的异步相关知识

    js里面一共有以下异步的解决方案

传统的回调

    省略 。。。。

生成器

     Generator 函数是 ES6 提供的一种异步编程解决方案, 语法上,首先可以把它理解成,Generator 函数是一个状态机,封装了多个内部状态。执行 Generator 函数会返回一个遍历器对象,也就是说,Generator 函数除了状态机,还是一个遍历器对象生成函数。返回的遍历器对象,可以依次遍历 Generator 函数内部的每一个状态。

 怎么定义一个生成器

function* gen1(){yield 1;yield 2;
}function* test() {yield 'a';
}test.next(); // value: "a", done: false}调用 Generator 函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象,也就是遍历器对象(Iterator Object)。
每次调用遍历器对象的next方法,就会返回一个有着value和done两个属性的对象。   
value属性表示当前的内部状态的值,是yield表达式后面那个表达式的值; 
done属性是一个布尔值,表示是否遍历结束

       function* 在 JavaScript 中不是函数指针,而是一种特殊的函数语法,叫做生成器函数(Generator Function)

function* 是什么?

function* 用于定义一个生成器函数。生成器函数是一种可以暂停和恢复执行的特殊函数。它返回一个生成器对象(Generator Object),这个对象实现了迭代器(Iterator)协议。

      Generator 函数就是一个封装的异步任务,或者说是异步任务的容器。

异步操作需要暂停的地方,都用 yield 语句注明

调用 Generator 函数,返回的就是一个生成器(迭代器)对象。

然后调用迭代器对象的 next() 方法,就是按顺序执行代码,知道碰到下一个yield 关键字 ,然后返回,然后要可以执行next()  直到函数体内部流程执行完毕。

    next 方法的作用是分阶段执行 Generator 函数。每次调用 next 方法,会返回一个对象,表示当前阶段的信息( value 属性和 done 属性)value 属性是 yield 语句后面表达式的值,表示当前阶段的值;done 属性是一个布尔值,表示 Generator 函数是否执行完毕,即是否还有下一个阶段。

  如: 

        const gen1 = myGenerator(); // 返回一个生成器对象

        result=gen1.next()

        console.log(result.value,result.done); //如果没有执行完成的话 done是false

了解generator用法

function* Generator() {yield "1";yield Promise.resolve(2);return "3";
}
var gen = Generator();
console.log(gen); //返回一个指针对象 Generator {<suspended>}

调用next方法

let res1 = gen.next();
console.log(res1); // 返回当前阶段的值 { value: '1', done: false }let res2 = gen.next();
console.log(res2); // 返回当前阶段的值 { value: Promise { 2 }, done: false }res2.value.then((res) => {console.log(res); // 2
});let res3 = gen.next();
console.log(res3); // { value: '3', done: true }let res4 = gen.next();
console.log(res4); // { value: undefined, done: true }

async/await实现

    而async/await 是Generator+Promise方案的语法糖。async 函数的实现,就是将 Generator 函数和自动执行器(自动调用生成器的next()方法,直到最后一个),包装在一个函数里。

     generator 函数需要通过调用next()方法,才能往后执行到下一个yield,但是 async 函数却不需要,它能够自动向后执行。

     await 可以理解为是 async wait 的简写。await 必须出现在 async 函数内部,不能单独使用

await 字面上使得 JavaScript 等待,直到 promise 处理完成,然后将结果继续下去

     await不能工作在顶级作用域,需要将await代码包裹在一个async函数中

    也可以形象的理解: 当执行异步函数时,碰到await时就把这个await 后面的所有代码全部封装成一个promise对象,然后放在微任务队列中等待调用,这样也就类似await后面的代码都必须等待

await后面的一行代码执行完毕。依次这样处理异步函数中的其他代码。

    async/await的理解

  • async 函数的实现原理,就是将 Generator 函数和自动执行器,包装在一个函数里
  • async 函数执行结果返回的是一个Promise
  • async 函数就是将 Generator 函数的星号(*)替换成 async,将 yield 替换成await
  • async/await 就是 Generator 的语法糖,其核心逻辑是迭代执行next函数

async 函数的实现1

   async 函数的实现,就是将 Generator 函数和自动执行器,包装在一个函数里

//第一种
async function fn(args){// ...
}//第二种 等同于 一
function fn(args){ return spawn(function*() {// ...}); 
}

     所有的 async 函数都可以写成上面的第二种形式,其中的 spawn 函数就是自动执行器。

下面给出 spawn 函数的实现,基本就是前文自动执行器的翻版。

function spawn(genF) {return new Promise(function(resolve, reject) {var gen = genF(); //执行器function step(nextF) {try {var next = nextF();} catch(e) {return reject(e); }if(next.done) {return resolve(next.value);} Promise.resolve(next.value).then(function(v) {step(function() { return gen.next(v); });      }, function(e) {step(function() { return gen.throw(e); });});}step(function() { return gen.next(undefined); });});
}
综合上面的代码的完整实例
<html><head><title>Test</title></head><body><div id="container"></div></body><script>function testfun(){console.log('testfun')}function fn(args){ return spawn(function*() {yield testfun();yield testfun();}); }function spawn(genF) {return new Promise(function(resolve, reject) {var gen = genF(); //执行器function step(nextF) {try {var next = nextF();} catch(e) {return reject(e); }if(next.done) {//如果没有其他迭代了 就把最开始的Promise对象的状态设置为ffulfilled,然后返回结果//resolve 返回结果会传递给then方法中的onFulfilled函数,也就是这里的next.valuereturn resolve(next.value);} //当前迭代器 没有执行完毕 就继续抛出一个Promise对象,然后继续执行迭代器中的下一个yield表达式//直到所有的yield表达式都执行完毕Promise.resolve(next.value).then(function(v) {step(function() { return gen.next(v); });      }, function(e) {step(function() { return gen.throw(e); });});}step(function() { return gen.next(undefined); });});}fn()</script>
</html>
其他实例分析
// Generator 函数,依次读取两个文件。const fs = require('fs');const readFile = function (fileName) {return new Promise(function (resolve, reject) {fs.readFile(fileName, function(error, data) {if (error) return reject(error);resolve(data);});});
};const gen = function* () {const f1 = yield readFile('/etc/fstab');const f2 = yield readFile('/etc/shells');console.log(f1.toString());console.log(f2.toString());
};
==================================
写成async函数,就是下面这样。const asyncReadFile = async function () {const f1 = await readFile('/etc/fstab');const f2 = await readFile('/etc/shells');console.log(f1.toString());console.log(f2.toString());
};一比较就会发现,async函数就是将 Generator 函数的星号(*)替换成async,将yield替换成await,仅此而已。async函数对 Generator 函数的改进
  • 当一个 async 函数中有多个 await命令时,如果不想因为一个出错而导致其与的都无法执行,应将await放在try...catch语句中执行
async function testAwait () {try {await func1()await func2()await func3()} catch (error) {console.log(error)}
}
  • 并发执行 await 命令: await 是顺序执行的,Promise.all() 是并行的
// 方法 1
let [res1, res2] = await Promise.all([func1(), func2()])// 方法 2
let func1Promise = func1()
let func2Promise = func2()
let res1 = await func1Promise
let res2 = await func2Promise
  • async方法: 一个class方法同样能够使用async,只需要将async放在它之前就可以

就像这样:

class Waiter {async wait () {return await Promise.resolve(1)}
}
new Waiter().wait().then(alert) // 1=======================# Class methods:
class Storage {constructor() {this.cachePromise = caches.open('avatars');}async getAvatar(name) {const cache = await this.cachePromise;return cache.match(`/avatars/${name}.jpg`);}
}const storage = new Storage();
storage.getAvatar('jaffathecake').then(…);
  • sleep(): 让线程休眠一段时间
// wait ms milliseconds
function wait(ms) {return new Promise(r => setTimeout(r, ms));
}async function hello() {await wait(500);return 'world';
}
业务场景举例

     你有三个请求需要发生,第三个请求是依赖于第二个请求的解构第二个请求依赖于第一个请求的结果。若用 ES5实现会有3层的回调,若用Promise 实现至少需要3个then。一个是代码横向发展,另一个是纵向发展。给出 async-await 的实现

//我们仍然使用 setTimeout 来模拟异步请求
function sleep(second, param) {return new Promise((resolve, reject) => {setTimeout(() => {resolve(param);}, second);})
}async function test() {let result1 = await sleep(2000, 'req01');let result2 = await sleep(1000, 'req02' + result1);let result3 = await sleep(500, 'req03' + result2);console.log(`${result3}${result2}${result1}`);
}test();

Promise的基本原理

介绍

      Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。Promise的异步在js中是通过微任务的方式构建的,Promise的.then() 会触发生成一个微任务,放在队列中。

      所谓Promise,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。

  • 首先在new Promise时,需要传入一个回调函数executor函数,Promise的主要业务流程都在executor函数中执行。
  • 如果运行在excutor函数中的业务执行成功了,会调用resolve(手动调用)函数;如果执行失败了,则调用reject函数。
  • Promise的状态不可逆,同时调用resolve函数和reject函数,默认会采取第一次调用的结果。
  • 它有constructor构造函数,还有catchfinallythen三个方法
  • 首先我们在调用Promise时,会返回一个Promise对象。

Promise/A+的规范比较多,在这列出一下核心的规范。Promise/A+规范

  • promise有三个状态:pending,fulfilled,rejected,默认状态是pending。
  • promise有一个value保存成功状态的值,有一个reason保存失败状态的值,可以是undefined/thenable/promise。
  • promise只能从pending到rejected, 或者从pending到fulfilled,状态一旦确认,就不会再改变。
  • promise 必须有一个then方法,then接收两个参数,分别是promise成功的回调onFulfilled, 和promise失败的回调onRejected。
  • 如果then中抛出了异常,那么就会把这个异常作为参数,传递给下一个then的失败的回调onRejected。

知识点1

new Promise((resolve, reject) => {console.log("tst111")
})///Promise 对象创建的时候 传入的参数 函数 会被同步执行,就是说在当前宏任务同步执行。

知识点2

const resolvePromise = Promise.resolve(1); //立刻返回一个fulled状态的Promise
//执行then 产生一个微任务
resolvePromise.then((res) => console.log(res)); 
console.log('test');先输出 test 
然后是1

  其他学习记录:

https://blog.csdn.net/binqian/article/details/145742026?spm=1011.2415.3001.5331

https://blog.csdn.net/binqian/article/details/145737399?spm=1011.2415.3001.5331

https://blog.csdn.net/binqian/article/details/145658887?spm=1011.2415.3001.5331

参考:

3.3.1 Async---Await原理 | 大前端

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JVM字节码文件结构

Class文件结构class文件是二进制文件&#xff0c;这里要介绍的是这个二级制文件的结构。思考&#xff1a;一个java文件编译成class文件&#xff0c;如果要描述一个java文件&#xff0c;需要哪些信息呢&#xff1f;基本信息&#xff1a;类名、父类、实现哪些接口、方法个数、每个…

11.web api 2

5. 操作元素属性 5.1操作元素常用属性 &#xff1a;通过 JS 设置/修改标签元素属性&#xff0c;比如通过 src更换 图片最常见的属性比如&#xff1a; href、title、src 等5.2 操作元素样式属性 &#xff1a;通过 JS 设置/修改标签元素的样式属性。使用 className 有什么好处&a…

java中数组和list的区别是什么?

在Java中&#xff0c;数组&#xff08;Array&#xff09;和List&#xff08;通常指java.util.List接口的实现类&#xff0c;如ArrayList、LinkedList&#xff09;是两种常用的容器&#xff0c;但它们在设计、功能和使用场景上有显著区别。以下从核心特性、使用方式等方面详细对…

Python爬取推特(X)的各种数据

&#x1f31f; Hello&#xff0c;我是蒋星熠Jaxonic&#xff01; &#x1f308; 在浩瀚无垠的技术宇宙中&#xff0c;我是一名执着的星际旅人&#xff0c;用代码绘制探索的轨迹。 &#x1f680; 每一个算法都是我点燃的推进器&#xff0c;每一行代码都是我航行的星图。 &#x…

Oracle数据库文件管理与空间问题解决指南

在Oracle数据库运维中&#xff0c;表空间、数据文件及相关日志文件的管理是保障数据库稳定运行的核心环节。本文将系统梳理表空间与数据文件的调整、关键文件的移动、自动扩展配置&#xff0c;以及常见空间不足错误的排查解决方法&#xff0c;为数据库管理员提供全面参考。 一、…

华为实验综合小练习

描述&#xff1a; 1 内网有A、B、C 三个部门。所在网段如图所示。 2 内网服务器配置静态IP,网关192.168.100.1。 3 sw1和R1之间使用vlan200 192.168.200.0/30 互联。 4 R1向运营商申请企业宽带并申请了5个公网IP&#xff1a;200.1.1.1-.5子网掩码 255.255.255.248&#xff0c;网…

Flink面试题及详细答案100道(1-20)- 基础概念与架构

《前后端面试题》专栏集合了前后端各个知识模块的面试题&#xff0c;包括html&#xff0c;javascript&#xff0c;css&#xff0c;vue&#xff0c;react&#xff0c;java&#xff0c;Openlayers&#xff0c;leaflet&#xff0c;cesium&#xff0c;mapboxGL&#xff0c;threejs&…

爬虫逆向之滑块验证码加密分析(轨迹和坐标)

本文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的。否则由此产生的一切后果均与作者无关&#xff01;在爬虫开发过程中&#xff0c;滑块验证码常常成为我们获取数据的一大阻碍。而滑块验证码的加密方式多种多样&#xff0c;其中轨迹加密和坐标加密是比较常见的…

微信小程序实现导航至目的地

本人做的导航页面相关功能和效果的代码 javascript相关 Page({data: {markers: [],latitude: , // 中心点坐标longitude: ,FIXED_LAT: 31.2304, // 1. 写死的目标点坐标, 示例&#xff1a;上海人民广场FIXED_LNG: 121.4737},onLoad: function () {// 如果要显示地图可以看onLo…

中国科学社简史

中国科学社简史中国科学社&#xff0c;作为中国近代史上第一个民间综合性科学团体&#xff0c;为中国现代科学文化事业的发展做出了卓越贡献。其历程不仅见证了中国科学从萌芽到蓬勃发展的转变&#xff0c;还反映了中国科学体制化的艰难探索与辉煌成就。中国科学社的起源可追溯…

若尔当型,Jordon Form

文章目录一、相似二、若尔当型1.1 认识若尔当型1.2 凯莱-哈密顿定理 (Cayley-Hamilton Theorem)一、相似 Every matrix CB−1ABC B^{-1}ABCB−1AB has the same eigenvalues as A. These C’s are similar to A. 任意一个矩阵C&#xff0c;满足 CB−1ABC B^{-1}ABCB−1AB 都和…

解决uni-app微信小程序编译报错:unexpected character `1`

问题原因在uni-app微信小程序开发中&#xff0c;当template模板中包含英文符号<或>时&#xff0c;微信小程序的编译器会将这些符号误认为是HTML标签的开闭符号&#xff0c;从而导致类似unexpected character 1的编译错误。错误示例<view class"plan-bmi">…

[Linux] RAID存储技术

目录 RAID实现方式 RAID 0 RAID 1 RAID 5 RAID 10 管理RAID0 创建RAID 查看RAID 格式化和挂载 删除RAID 管理RAID1 创建RAID 查看RAID 格式化和挂载 增加热备盘 模拟故障 删除故障磁盘 删除RAID 管理RAID5 创建RAID 查看RAID md5设备划分分区 RAID实现方…

程序设计|C语言教学——C语言基础4:进阶

一、预处理指令预处理指令在编译前执行&#xff0c;除了#include&#xff0c;还有以下常用指令&#xff1a;1. #define 宏定义无参宏&#xff1a;定义常量或代码片段&#xff0c;编译时直接替换&#xff08;无类型检查&#xff09;。#define PI 3.1415926 // 定义常量 #define…

数据结构之heap算法

文章目录前言1. heap结构概述2. push_heap3. pop_heap4. sort_heap5. make_heap前言 heap这种数据结构&#xff0c;允许用户以任何次序将任何数据放入该结构中&#xff0c;但是最后取出数据的时候一定是权值最高&#xff08;或者最低&#xff09;的元素。主要和实现有关&#x…

MCU 软件断点调试注意事项!!!

——为什么调试器会在运行中改我的Flash程序&#xff1f;调试单片机时&#xff0c;很多人都有这样的疑问&#xff1a;明明我在调试前刷进去的固件是好的&#xff0c;为什么加了一个断点之后&#xff0c;调试器居然去改了 Flash&#xff1f; 如果我拔掉调试器&#xff0c;这个固…

启发式合并 + 莫队 恋恋的心跳大冒险

题目来源&#xff1a;2025 Wuhan University of Technology Programming Contest 比赛链接&#xff1a;Dashboard - 2025 Wuhan University of Technology Programming Contest - Codeforces 题目大意&#xff1a; Solution&#xff1a; 首先肯定要预处理出以每个节点为起点…

JCTools 无锁并发队列基础:ConcurrentCircularArrayQueue

ConcurrentCircularArrayQueue ConcurrentCircularArrayQueue 是一个抽象类&#xff0c;它为基于数组的并发循环队列提供了基础功能。从其命名可以看出几个关键特性&#xff1a;​​Concurrent​​&#xff1a;常指无锁并发。​​Circular Array​​&#xff1a;内部使用循环数…

力扣(LeetCode) ——622. 设计循环队列(C语言)

题目&#xff1a;622. 设计循环队列示例1&#xff1a; MyCircularQueue circularQueue new MyCircularQueue(3); // 设置长度为 3 circularQueue.enQueue(1); // 返回 true circularQueue.enQueue(2); // 返回 true circularQueue.enQueue(3); // 返回 true circularQueue.…

在JVM跑JavaScript脚本 | Oracle GraalJS 简介与实践

这是2024年初的 GraalVM 系列博文&#xff0c;当时写了大纲&#xff0c;知道一年半后的现在才得以完成发布&#x1f604; 1、概述 实话说&#xff0c;标题的场景为小众需求&#xff0c;日常开发基本用不到&#xff0c;我是最近在做一个低代码轮子玩具 app-meta 需要实现 FaaS&…