Deprecated: imwpcache\f884414bce24ee67f\f73723ec7b1919fa5::__construct(): Implicitly marking parameter $YECBGYFECGEAFWHA as nullable is deprecated, the explicit nullable type must be used instead in /www/wwwroot/www.chuangxiangniao.com/wp-content/plugins/imwpcache-dist/build/f884414bce24ee67ff73723ec7b1919fa5.php on line 2

Deprecated: imwpcache\f884414bce24ee67f\f73723ec7b1919fa5::__construct(): Implicitly marking parameter $BBWFDDBHHYHDXXAB as nullable is deprecated, the explicit nullable type must be used instead in /www/wwwroot/www.chuangxiangniao.com/wp-content/plugins/imwpcache-dist/build/f884414bce24ee67ff73723ec7b1919fa5.php on line 2
浅析Promise、Generator和Async间的差异_创想鸟

浅析Promise、Generator和Async间的差异

promiseasync/await函数都是用来解决javascript中的异步问题的,那么它们之间有什么区别吗?下面本篇文章就来给大家介绍一下promise、generator和async间的差异,希望对大家有所帮助!

浅析Promise、Generator和Async间的差异

我们知道PromiseAsync/await函数都是用来解决JavaScript中的异步问题的,从最开始的回调函数处理异步,到Promise处理异步,到Generator处理异步,再到Async/await处理异步,每一次的技术更新都使得JavaScript处理异步的方式更加优雅,从目前来看,Async/await被认为是异步处理的终极解决方案,让JS的异步处理越来越像同步任务。异步编程的最高境界,就是根本不用关心它是不是异步

异步解决方案的发展历程

1.回调函数

从早期的Javascript代码来看,在ES6诞生之前,基本上所有的异步处理都是基于回调函数函数实现的,你们可能会见过下面这种代码:

ajax('aaa', () => {    // callback 函数体    ajax('bbb', () => {        // callback 函数体        ajax('ccc', () => {            // callback 函数体        })    })})

没错,在ES6出现之前,这种代码可以说是随处可见。它虽然解决了异步执行的问题,可随之而来的是我们常听说的回调地狱问题:

没有顺序可言:嵌套函数执行带来的是调试困难,不利于维护与阅读耦合性太强:一旦某一个嵌套层级有改动,就会影响整个回调的执行

所以,为了解决这个问题,社区最早提出和实现了Promise,ES6将其写进了语言标准,统一了用法。

2.Promise

Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。它就是为了解决回调函数产生的问题而诞生的。

有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise对象提供统一的接口,使得控制异步操作更加容易。

所以上面那种回调函数的方式我们可以改成这样:(前提是ajax已用Promise包装)

ajax('aaa').then(res=>{  return ajax('bbb')}).then(res=>{  return ajax('ccc')})

通过使用Promise来处理异步,比以往的回调函数看起来更加清晰了,解决了回调地狱的问题,Promisethen的链式调用更能让人接受,也符合我们同步的思想。

但Promise也有它的缺点:

Promise的内部错误使用try catch捕获不到,只能只用then的第二个回调或catch来捕获

let protry{    pro = new Promise((resolve,reject) => {        throw Error('err....')    })}catch(err){    console.log('catch',err) // 不会打印}pro.catch(err=>{    console.log('promise',err) // 会打印})

Promise一旦新建就会立即执行,无法取消

之前写过一篇,讲解了Promise如何使用以及内部实现原理。对Promise还不太理解的同学可以看看~

从如何使用到如何实现一个Promisehttps://juejin.cn/post/7051364317119119396

3.Generator

Generator 函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同。Generator 函数将 JavaScript 异步编程带入了一个全新的阶段。

声明

与函数声明类似,不同的是function关键字与函数名之间有一个星号,以及函数体内部使用yield表达式,定义不同的内部状态(yield在英语里的意思就是“产出”)。

function* gen(x){ const y = yield x + 6; return y;}// yield 如果用在另外一个表达式中,要放在()里面// 像上面如果是在=右边就不用加()function* genOne(x){  const y = `这是第一个 yield 执行:${yield x + 1}`; return y;}

执行

const g = gen(1);//执行 Generator 会返回一个Object,而不是像普通函数返回return 后面的值g.next() // { value: 7, done: false }//调用指针的 next 方法,会从函数的头部或上一次停下来的地方开始执行,直到遇到下一个 yield 表达式或return语句暂停,也就是执行yield 这一行// 执行完成会返回一个 Object,// value 就是执行 yield 后面的值,done 表示函数是否执行完毕g.next() // { value: undefined, done: true }// 因为最后一行 return y 被执行完成,所以done 为 true

调用 Generator 函数后,该函数并不执行,返回的也不是函数运行结果,而是一个指向内部状态的指针对象,也就是遍历器对象(Iterator Object)。下一步,必须调用遍历器对象的next方法,使得指针移向下一个状态。

所以上面的回调函数又可以写成这样:

function *fetch() {    yield ajax('aaa')    yield ajax('bbb')    yield ajax('ccc')}let gen = fetch()let res1 = gen.next() // { value: 'aaa', done: false }let res2 = gen.next() // { value: 'bbb', done: false }let res3 = gen.next() // { value: 'ccc', done: false }let res4 = gen.next() // { value: undefined, done: true } done为true表示执行结束

由于 Generator 函数返回的遍历器对象,只有调用next方法才会遍历下一个内部状态,所以其实提供了一种可以暂停执行的函数。yield表达式就是暂停标志。

遍历器对象的next方法的运行逻辑如下。

(1)遇到yield表达式,就暂停执行后面的操作,并将紧跟在yield后面的那个表达式的值,作为返回的对象的value属性值。

(2)下一次调用next方法时,再继续往下执行,直到遇到下一个yield表达式。

(3)如果没有再遇到新的yield表达式,就一直运行到函数结束,直到return语句为止,并将return语句后面的表达式的值,作为返回的对象的value属性值。

(4)如果该函数没有return语句,则返回的对象的value属性值为undefined

美间AI 美间AI

美间AI:让设计更简单

美间AI 45 查看详情 美间AI

yield表达式本身没有返回值,或者说总是返回undefinednext方法可以带一个参数,该参数就会被当作上一个yield表达式的返回值。

怎么理解这句话?我们来看下面这个例子:

function* foo(x) {  var y = 2 * (yield (x + 1));  var z = yield (y / 3);  return (x + y + z);}var a = foo(5);a.next() // Object{value:6, done:false}a.next() // Object{value:NaN, done:false}a.next() // Object{value:NaN, done:true}var b = foo(5);b.next() // { value:6, done:false }b.next(12) // { value:8, done:false }b.next(13) // { value:42, done:true }

由于yield没有返回值,所以(yield(x+1))执行后的值是undefined,所以在第二次执行a.next()是其实是执行的2*undefined,所以值是NaN,所以下面b的例子中,第二次执行b.next()时传入了12,它会当成第一次b.next()的执行返回值,所以b的例子中能够正确计算。这里不能把next执行结果中的value值与yield返回值搞混了,它两不是一个东西

yield与return的区别

相同点:

都能返回语句后面的那个表达式的值都可以暂停函数执行

区别:

一个函数可以有多个 yield,但是只能有一个 returnyield 有位置记忆功能,return 没有

4.Async/await

Async/await其实就是上面Generator的语法糖,async函数其实就相当于funciton *的作用,而await就相当与yield的作用。而在async/await机制中,自动包含了我们上述封装出来的spawn自动执行函数。

所以上面的回调函数又可以写的更加简洁了:

async function fetch() {  await ajax('aaa')    await ajax('bbb')    await ajax('ccc')}// 但这是在这三个请求有相互依赖的前提下可以这么写,不然会产生性能问题,因为你每一个请求都需要等待上一次请求完成后再发起请求,如果没有相互依赖的情况下,建议让它们同时发起请求,这里可以使用Promise.all()来处理

async函数对Generator函数的改进,体现在以下四点:

内置执行器:async函数执行与普通函数一样,不像Generator函数,需要调用next方法,或使用co模块才能真正执行语意化更清晰:asyncawait,比起星号和yield,语义更清楚了。async表示函数里有异步操作,await表示紧跟在后面的表达式需要等待结果。适用性更广:co模块约定,yield命令后面只能是 Thunk 函数或 Promise 对象,而async函数的await命令后面,可以是 Promise 对象和原始类型的值(数值、字符串和布尔值,但这时会自动转成立即 resolved 的 Promise 对象)。返回值是Promise:async函数的返回值是 Promise 对象,这比 Generator 函数的返回值是 Iterator 对象方便多了。你可以用then方法指定下一步的操作。

async函数

async函数的返回值为Promise对象,所以它可以调用then方法

async function fn() {  return 'async'}fn().then(res => {  console.log(res) // 'async'})

await表达式

await 右侧的表达式一般为 promise 对象, 但也可以是其它的值

如果表达式是 promise 对象, await 返回的是 promise 成功的值

如果表达式是其它值, 直接将此值作为 await 的返回值

await后面是Promise对象会阻塞后面的代码,Promise 对象 resolve,然后得到 resolve 的值,作为 await 表达式的运算结果

所以这就是await必须用在async的原因,async刚好返回一个Promise对象,可以异步执行阻塞

function fn() {    return new Promise((resolve, reject) => {        setTimeout(() => {            resolve(1000)        }, 1000);    })}function fn1() { return 'nanjiu' }async function fn2() {    // const value = await fn() // await 右侧表达式为Promise,得到的结果就是Promise成功的value    // const value = await '南玖'    const value = await fn1()    console.log('value', value)}fn2() // value 'nanjiu'

异步方案比较

后三种方案都是为解决传统的回调函数而提出的,所以它们相对于回调函数的优势不言而喻。而async/await又是Generator函数的语法糖。

Promise的内部错误使用try catch捕获不到,只能只用then的第二个回调或catch来捕获,而async/await的错误可以用try catch捕获Promise一旦新建就会立即执行,不会阻塞后面的代码,而async函数中await后面是Promise对象会阻塞后面的代码。async函数会隐式地返回一个promise,该promisereosolve值就是函数return的值。使用async函数可以让代码更加简洁,不需要像Promise一样需要调用then方法来获取返回值,不需要写匿名函数处理Promise的resolve值,也不需要定义多余的data变量,还避免了嵌套代码。

说了这么多,顺便看个题吧~

console.log('script start')async function async1() {    await async2()    console.log('async1 end')}async function async2() {    console.log('async2 end')}async1()setTimeout(function() {    console.log('setTimeout')}, 0)new Promise(resolve => {    console.log('Promise')    resolve()}).then(function() {    console.log('promise1')}).then(function() {    console.log('promise2')})console.log('script end')

解析:

打印顺序应该是: script start -> async2 end -> Promise -> script end -> async1 end -> promise1 -> promise2 -> setTimeout

老规矩,全局代码自上而下执行,先打印出script start,然后执行async1(),里面先遇到await async2(),执行async2,打印出async2 end,然后await后面的代码放入微任务队列,接着往下执行new Promise,打印出Promise,遇见了resolve,将第一个then方法放入微任务队列,接着往下执行打印出script end,全局代码执行完了,然后从微任务队列中取出第一个微任务执行,打印出async1 end,再取出第二个微任务执行,打印出promise1,然后这个then方法执行完了,当前Promise的状态为fulfilled,它也可以出发then的回调,所以第二个then这时候又被加进了微任务队列,然后再出微任务队列中取出这个微任务执行,打印出promise2,此时微任务队列为空,接着执行宏任务队列,打印出setTimeout

解题技巧:

无论是then还是catch里的回调内容只要代码正常执行或者正常返回,则当前新的Promise实例为fulfilled状态。如果有报错或返回Promise.reject()则新的Promise实例为rejected状态。fulfilled状态能够触发then回调rejected状态能够触发catch回调执行async函数,返回的是Promise对象await相当于Promise的then并且同一作用域下await下面的内容全部作为then中回调的内容异步中先执行微任务,再执行宏任务

【相关推荐:javascript学习教程

以上就是浅析Promise、Generator和Async间的差异的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/553249.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
电脑屏幕出现故障的原因及解决方案
上一篇 2025年11月9日 21:06:22
新质生产力代表 华为智慧 PC 成新华网 2024 两会报道指定用机
下一篇 2025年11月9日 21:06:29

相关推荐

  • PHP错误日志怎么查看_PHP错误日志定位与查看方法

    要查看PHP错误日志,首先确定php.ini中error_log路径,若未设置则检查Web服务器(如Apache/Nginx)错误日志;确保log_errors=On、error_reporting合理配置,并通过tail、grep等工具分析日志,结合框架日志和系统日志(如syslog)全面定位问题…

    2026年9月21日
    200
  • 谷歌浏览器官方在线访问 最新版Chrome官网登录

    谷歌浏览器官方在线访问入口是https://www.google.cn/chrome/,提供简洁界面、跨设备同步、高效内核、安全防护和丰富扩展生态。 谷歌浏览器官方在线访问入口在哪里?这是不少网友都关注的,接下来由PHP小编为大家带来最新版Chrome官网登录地址,想要获取纯净浏览体验的网友一起随小…

    2026年9月21日
    200
  • JavaScript中的模块联邦如何实现微前端的代码共享?

    模块联邦通过运行时动态加载实现微前端代码共享,无需打包公共依赖。使用 ModuleFederationPlugin 配置 name、remotes、exposes 和 shared,使应用可暴露或引入远程模块,支持组件、工具函数及状态管理共享,提升复用性并减少冗余。 模块联邦通过在构建时让不同应用直…

    2026年9月21日
    200
  • UC浏览器网页上的文字无法选中复制怎么办 UC浏览器解决网页文字禁止复制问题

    答案:可通过开发者工具、阅读模式、打印预览、OCR识别或自定义脚本解除UC浏览器网页复制限制。具体操作依次为:开启开发者工具并执行JavaScript代码解除限制;启用阅读模式净化页面内容;使用打印预览重新渲染页面以选中文字;对截图应用OCR技术提取文本;添加书签脚本自动移除禁用选择的代码,从而实现…

    2026年9月21日
    100
  • JavaScript中的尾调用优化(TCO)在ES6中如何工作?

    尾调用是指函数的最后一个动作调用另一个函数,ES6引入尾调用优化以重用栈帧、避免内存溢出,支持真正的尾递归,如阶乘函数通过累积参数实现。 尾调用优化(Tail Call Optimization, TCO)是ES6引入的一项语言特性,目的是在特定条件下重用函数调用栈帧,避免不必要的内存增长,从而支持…

    2026年9月21日
    200
  • JSF应用中Markdown文档动态链接处理指南

    本教程旨在解决jsf web应用程序中集成markdown文档时,如何动态处理内部链接以实现页面局部更新的问题。通过结合服务器端markdown渲染和客户端javascript事件监听,我们可以拦截markdown生成的html链接点击事件,利用ajax异步加载并渲染目标markdown文件,从而在…

    2026年9月21日
    600
  • 如何自定义代码的格式化规则?

    自定义代码格式化规则需选择合适工具并配置文件实现统一风格。1. 根据语言选用主流工具如Prettier、Black、clang-format等;2. 在项目根目录创建对应配置文件如.prettierrc、.eslintrc.js或pyproject.toml,定义缩进、引号、行宽等规则;3. 将配置…

    2026年9月21日
    100
  • 怎样在VSCode中快速生成注释文档?

    安装插件如Document This和Koro File Header,通过快捷键在VSCode中快速生成函数及文件注释,支持自定义模板,提升注释效率与规范性。 在 VSCode 中快速生成注释文档,主要依赖插件和快捷键配合代码语言特性来实现。不同编程语言支持方式略有差异,但核心思路是使用智能提示和…

    2026年9月21日
    200
  • 如何为特定语言配置VSCode的语法高亮?

    安装对应语言扩展并关联文件类型,可实现VSCode语法高亮。首先通过扩展面板安装目标语言插件,如Ruby或Rust;若文件扩展名未被识别,需手动将扩展名关联至正确语言;最后可在settings.json中配置editor.tokenColorCustomizations来自定义高亮颜色,确保语法解析…

    2026年9月21日
    100
  • 为什么VSCode的语法高亮有时会失效?

    语法高亮失效通常由语言模式识别错误、扩展冲突或配置问题导致。1. 检查右下角语言模式并手动切换为正确类型,确保文件有正确扩展名;2. 禁用近期安装的扩展或以 code –disable-extensions 启动排查冲突;3. 切换至默认主题并检查 settings.json 是否覆盖颜…

    2026年9月21日
    600
  • VSCode的括号着色功能如何帮助你避免语法错误?

    VSCode括号着色功能通过彩色高亮匹配括号,帮助用户直观识别嵌套结构、提升代码可读性,并快速发现遗漏或多余括号,减少语法错误。 VSCode的括号着色功能通过视觉方式帮你快速识别代码中的匹配和嵌套结构,减少语法错误的发生。当你在编写代码时,成对出现的括号(如()、[]、{})会被高亮显示为相同或相…

    2026年9月21日
    000
  • 如何制作抖音点单小程序:全面指南与实用技巧

    引言: 随着移动互联网的飞速发展,抖音已不仅仅是短视频平台,更成为商家连接用户的重要入口。越来越多企业开始关注抖音点单小程序的搭建,以提升服务效率和用户体验。本文将为您系统讲解抖音点单小程序的制作流程,并分享实用技巧与真实案例,助您快速打造专属的小程序,实现流量变现与销售增长。 1. 明确核心需求与…

    2026年9月21日
    200
  • PHP播放HLS视频流的方法_PHP播放HLS视频流方法

    答案:PHP通过权限控制和文件代理实现HLS流安全分发,前端使用HTML5视频标签和hls.js播放。具体描述:HLS将视频切为.ts片段并用.m3u8索引,PHP后端可校验用户权限、防止盗链,动态输出.m3u8或.ts内容;前端通过video标签加载stream.php?id=1,结合hls.js…

    2026年9月20日
    000
  • VSCode有哪些必备的插件?

    EditorConfig for VS Code统一代码风格,2. Prettier自动格式化多语言代码,3. ESLint检查JS/TS错误并集成Prettier,4. GitLens增强Git可视化,5. Path Intellisense补全文件路径,6. 括号高亮提升嵌套识别,7. Auto…

    2026年9月20日
    1000
  • Via浏览器怎么让地址栏显示完整的网址链接_Via浏览器显示完整网址的设置方法

    1、打开Via浏览器设置,进入高级设置中的地址栏选项,开启“显示完整网址”功能;2、在外观设置中关闭简洁模式或极简地址栏,以恢复协议头和路径显示;3、高级用户可借助自定义脚本强制输出完整URL,通过工具箱添加执行脚本实现。 如果您在使用Via浏览器时发现地址栏默认只显示域名而隐藏了完整的网址链接,可…

    2026年9月20日
    000
  • ChatGPT代码会出错吗_AI编程中5个常见错误及解决方法

    AI编程中常见错误包括语法不匹配、逻辑遗漏、API误用、安全漏洞和集成困难,需通过版本明确、测试验证、文档核对、安全扫描和上下文补充等方式解决,结合人工审查与测试才能确保代码质量。 ☞☞☞AI 智能聊天, 问答助手, AI 智能搜索, 免费无限量使用 DeepSeek R1 模型☜☜☜ ChatGP…

    2026年9月20日
    100
  • edge浏览器无法打开本地HTML文件或显示空白怎么办_Edge浏览器打开本地HTML文件失败解决方法

    1、检查文件路径并选择Edge打开,确保路径为纯英文;2、在edge://flags中启用“Allow file access from files”;3、使用开发者工具排查资源加载错误;4、通过命令行启动Edge绕过安全限制;5、推荐使用npx live-server搭建本地服务器运行HTML文件…

    2026年9月20日
    000
  • 怎样在VSCode中为不同文件类型设置不同的缩进?

    在VSCode中可为不同文件类型设置缩进,通过settings.json按语言ID配置,如Python用4空格、HTML用2空格,保存后自动生效并覆盖全局设置。 在 VSCode 中为不同文件类型设置不同的缩进,可以通过配置语言特定的设置来实现。VSCode 支持按语言 ID 覆盖编辑器的缩进行为,…

    2026年9月20日
    100
  • Via浏览器怎么设置在新标签页打开链接_Via浏览器调整链接打开方式的方法

    Via浏览器可通过长按链接选择“在新标签页中打开”;2. 在设置中修改“标签页行为”为始终在新标签页打开;3. 通过手势设置自定义快捷操作;4. 高级用户可启用JavaScript脚本强制所有链接在新标签页打开。 如果您在浏览网页时希望链接能够在新标签页中打开,以方便多任务处理或保留当前页面,可以通…

    2026年9月20日
    200
  • VSCode的悬浮提示信息如何自定义?

    通过JSDoc或docstring添加注释可直接影响VSCode悬浮提示内容,如JavaScript/TypeScript中使用/* /格式、Python中使用三引号文档字符串,配合Pylance等扩展增强显示;安装语言支持扩展可提升提示丰富度;高级场景可通过开发自定义语言服务器,在textDocu…

    2026年9月20日
    500

发表回复

登录后才能评论
关注微信