【JS技巧】使用顶层 await 简化 JS 代码

2024-08-25 20:08

本文主要是介绍【JS技巧】使用顶层 await 简化 JS 代码,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

前言

JavaScript 是一种流行的编程语言,最初被设计为单线程和同步的语言,意味着程序需要在不等待获取外部资源或者耗时的计算的情况下一步一步的运行。如果脚本需要这样的资源或计算,这种同步行为将导致错误。这会阻塞队列中的所有其他进程运行,无论它们是否依赖于那些阻塞中的任务。
但很久以前,JavaScript 引入了一个机制让其可以在等待外部资源或者耗时任务的时候去执行其剩余的代码。这种异步行为是通过在函数上使用回调或 promise 来实现的。

什么是回调和promise

我将通过代码来解释这些概念。如果你已经知道回调和 promise 是什么,请直接跳到顶层 await 部分和示例应用程序。

回调

在回调中,一个函数作为参数传递给另一个函数;因此,下面 addEventListener 函数中的第二个参数是回调函数。此回调将等待第一个 click 事件发生,然后才执行第二个参数。

const x = document.getElementsByTagName('Button');
x[0].addEventListener('click',() =>{alert("I was clicked")})

这种等待行为使代码异步。这与同步代码不同,它可以接着一步一步的运行,而不需要等待资源下载或者耗时进程结束。但要注意,并非所有回调函数都是异步的。

Promise

Promise 类似于回调,它将函数附加到返回的对象上。但Promise和回调也有不同,Promise 是专门为异步方法设计的。它们只有一个参数和一个用来获得返回结果的 then() 函数。此外,它还可以链式的附加多个.then() 和 catch() 函数。

fetch('www.xyz.com/api')
.then((res)=>{let x = res.data; //do something with received data})
.catch((err)=>{console.log(err)})

Promise使用事件队列并严格遵循异步任务被链接的顺序来执行。

Async/await

Async/await 是对 Promises 的语法改进,以避免链式调用。它能使代码更清晰,更容易理解。await 关键字使代码暂停,直到 Promises 成功(resolved)或者失败(resolved)。

async function asyncwaitcode(){let getData = await axios('www.xyzdata.org/api')console.log(getData.data)
}

什么是顶层 await

以上所有示例都让功能块中的代码异步,没有一个在模块级工作。
但是,异步行为是可以在模块级别实现的。使用顶级 await 的模块会在异步初始化其命名空间后,再通知该模块的消费者继续执行它自己的代码。
下面的示例代码展示了如何使用顶级 await。

关于App

此应用将从新闻 API 中获取最热门的新闻数据,并在浏览器中呈现。用户还能以相关搜索词的进行新闻数据搜索。在开始之前,有几点需要注意:

  • 顶级await在node 13.3及更高版中被支持
  • 顶级await只在ECMAScript模块中支持,但Node.js和Express都是CmmonJS模块。CmmonJS不支持顶级await特性。所以我在代码中会使用import而不是require
  • 在node 14.x之前,顶级await不能直接使用,需要启用–harmony
  • 循环引用模块可能会导致死锁

示例

构建App

1.创建toplevelawait目录

$ mkdir toplevelawait

2.npm init初始化

$ npm init

3.package.json中增加"type": “module”,以支持ECMAScript模块

"author": "",
"license": "ISC",
"type": "module",

4.在toplevelawait目录下创建src目录。注意使用mjs作为文件后缀名。

$ touch app.mjs
$ touch exp.mjs
$ ls -1 src
app.mjs
exp.mjs

5.安装依赖axios, ejs和 express

$ npm install axios ejs express --save
exp.mjs中增加下面代码:
import express from "express"
export const exp = await express();

注意我们是在没有async的情况下使用await。这样express实例会先初始化后再导出給其他模块。你可以用这种方式等待模块中某个实例初始化后完成后,再执行依赖于该模块的代码。
如果一个模块包含了顶层await,那么它的父模块的执行会停止,直到promise完成。但他的兄弟模块会继续执行,和以前的同步模式相同。
要注意Node.js中的模块加载也是同步的,意味着他不能异步的等待资源加载。但你可以在加载或者处理资源的语句前加上await关键字来实现异步等待。

增加news APIs

这个应用使用两个免费的新闻API来获取数据。两个API支持回退依赖行为;如果一个API失败,另一个API将获取数据。这两个API都使用API密钥:

  • News API[1]
  • GNews API[2]
    在app.mjs文件中插入下面的代码。前面的目标导入axios和在exp.js被初始化的express实例。下一部分设置视图引擎,以便在浏览器中展示。
import { exp } from "./exp.mjs";
import axios from "axios"exp.set("view engine","ejs");
// dependency fall back
let response = "";
let site = true;
try{response = await axios('https://newsapi.org/v2/top-headlines?country=us&apiKey=your-api-key');  }catch{response = await axios("https://gnews.io/api/v3/top-news?token=your-api-key");site = false;}// Get top news
exp.get('/',function(req,res){let response0 = response.data.articles  res.render('main.ejs',{response0: response0, site:site})})// search news
exp.get('/search', function(req,res){res.render("searchnews.ejs")  
})
exp.get('/result', async(req, res)=>{let x = req.query.newtitlesearch;let response1 = {}let data = {}try{let url = 'https://newsapi.org/v2/everything?q='+x+'&apiKey=your-api-key'response1 =  await axios(url);}catch{let url = 'https://gnews.io/api/v3/search?q='+x+'&token=your-api-key'response1 =  await axios(url)}res.render('result.ejs', {response1: response1.data.articles, site: site})  
})
exp.listen(3000)

最重要的部分是这个try catch块,使用到了顶层await来等待axios去获取数据。如果由于任何原因,axios无法从第一个API获取数据,应用将使用第二个API获取数据。一旦它从API获得数据,express可以在主页上呈现它。

try{response = await axios(请求接口链接);}catch{response = await axios(请求接口链接);}

接下来提供了一个可以让用户进行搜索的路由:

// search news
exp.get('/search', function(req,res){res.render("../src/view/searchnews.ejs")  })

最后,另一个路径展示搜索结果:

exp.get('/result', async(req,res)=>{let x = req.query.newtitlesearch;let response1 = {}let data = {}try{let url = 'https://newsapi.org/v2/everything?q='+x+'&apiKey=your-api-key'response1 =  await axios(url);}catch{let url = 'https://gnews.io/api/v3/search?q='+x+'&token=your-api-key'response1 =  await axios(url)}res.render('../src/view/result.ejs', {response1: response1.data.articles , site: site})  
})

写前端界面

应用的最后一部分是为前端页面编写四个.ejs HTML文件。将这些文件保存在“view”文件夹中:

//header.ejs
<!DOCTYPE html>
<head><title>newapiapp</title><link type="text/css" rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" ></head><body>  <nav class="navbar navbar-default"><div class="container-fluid"><div class="navbar-header"><a class="navbar-brand" href="#">News app</a></div><div class="collapse navbar-collapse"><ul class="nav navbar-nav navbar-right"><li><a href="/">Main</a></li>                <li><a href="/search">Search</a></li>      </ul>      </div></div>  </nav>
//main.ejs
<%include('header');%>
<%let rows = response0%>
<%let siterep = site%><div name "container"><div class="row text-center" style="display:flex; flex-wrap:wrap"><% for(let i = 0; i < rows.length; i++){%><div class="col-md-3 col-sm-6 "><div class="thumbnail" ><a href="<%-rows[i].url %>"><img src = "<%= siterep ? rows[i].urlToImage :  rows[i].url  %>"></a>                            </div><div><%= rows[i].title%></div>                </div>    <% } %></div>  
</div>
//searchnews.ejs
<%- include('header');%><h1>Search news </h1><form action="/result" method="Get"><input type ="text" placeholder="news title search" name="newtitlesearch"></input><input type="submit" placeholder="submit"></input>        </form>
//result.ejs
<%- include('header');%>
<%let rows = response1%>
<%let siterep = site%><div name "container"><div class="row text-center" style="display:flex; flex-wrap:wrap"><% for(let i = 0; i < rows.length; i++){%><div class="col-md-3 col-sm-6 "><div class="thumbnail" ><a href="<%-rows[i].url %>"><img src = "<%= siterep ? rows[i].urlToImage :  rows[i].url  %>"></a>                            </div><div><%= rows[i].title%></div>                  </div>    <% } %></div>  
</div>

运行app

现在APP已经完成,你可以尝试下。
如果你在使用node.js v13.3 至 v14.0,运行:
$ node --harmony-top-level-await app.js
如果你在使用node.js v14.0以上,你不需要–harmony flag:
$ node app.js
如果你成功构建了这个app,那么恭喜你又学会了一个新的js特性

这篇关于【JS技巧】使用顶层 await 简化 JS 代码的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1106532

相关文章

C++使用栈实现括号匹配的代码详解

《C++使用栈实现括号匹配的代码详解》在编程中,括号匹配是一个常见问题,尤其是在处理数学表达式、编译器解析等任务时,栈是一种非常适合处理此类问题的数据结构,能够精确地管理括号的匹配问题,本文将通过C+... 目录引言问题描述代码讲解代码解析栈的状态表示测试总结引言在编程中,括号匹配是一个常见问题,尤其是在

Java中String字符串使用避坑指南

《Java中String字符串使用避坑指南》Java中的String字符串是我们日常编程中用得最多的类之一,看似简单的String使用,却隐藏着不少“坑”,如果不注意,可能会导致性能问题、意外的错误容... 目录8个避坑点如下:1. 字符串的不可变性:每次修改都创建新对象2. 使用 == 比较字符串,陷阱满

Python使用国内镜像加速pip安装的方法讲解

《Python使用国内镜像加速pip安装的方法讲解》在Python开发中,pip是一个非常重要的工具,用于安装和管理Python的第三方库,然而,在国内使用pip安装依赖时,往往会因为网络问题而导致速... 目录一、pip 工具简介1. 什么是 pip?2. 什么是 -i 参数?二、国内镜像源的选择三、如何

使用C++实现链表元素的反转

《使用C++实现链表元素的反转》反转链表是链表操作中一个经典的问题,也是面试中常见的考题,本文将从思路到实现一步步地讲解如何实现链表的反转,帮助初学者理解这一操作,我们将使用C++代码演示具体实现,同... 目录问题定义思路分析代码实现带头节点的链表代码讲解其他实现方式时间和空间复杂度分析总结问题定义给定

Linux使用nload监控网络流量的方法

《Linux使用nload监控网络流量的方法》Linux中的nload命令是一个用于实时监控网络流量的工具,它提供了传入和传出流量的可视化表示,帮助用户一目了然地了解网络活动,本文给大家介绍了Linu... 目录简介安装示例用法基础用法指定网络接口限制显示特定流量类型指定刷新率设置流量速率的显示单位监控多个

JavaScript中的reduce方法执行过程、使用场景及进阶用法

《JavaScript中的reduce方法执行过程、使用场景及进阶用法》:本文主要介绍JavaScript中的reduce方法执行过程、使用场景及进阶用法的相关资料,reduce是JavaScri... 目录1. 什么是reduce2. reduce语法2.1 语法2.2 参数说明3. reduce执行过程

如何使用Java实现请求deepseek

《如何使用Java实现请求deepseek》这篇文章主要为大家详细介绍了如何使用Java实现请求deepseek功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1.deepseek的api创建2.Java实现请求deepseek2.1 pom文件2.2 json转化文件2.2

Java调用DeepSeek API的最佳实践及详细代码示例

《Java调用DeepSeekAPI的最佳实践及详细代码示例》:本文主要介绍如何使用Java调用DeepSeekAPI,包括获取API密钥、添加HTTP客户端依赖、创建HTTP请求、处理响应、... 目录1. 获取API密钥2. 添加HTTP客户端依赖3. 创建HTTP请求4. 处理响应5. 错误处理6.

python使用fastapi实现多语言国际化的操作指南

《python使用fastapi实现多语言国际化的操作指南》本文介绍了使用Python和FastAPI实现多语言国际化的操作指南,包括多语言架构技术栈、翻译管理、前端本地化、语言切换机制以及常见陷阱和... 目录多语言国际化实现指南项目多语言架构技术栈目录结构翻译工作流1. 翻译数据存储2. 翻译生成脚本

C++ Primer 多维数组的使用

《C++Primer多维数组的使用》本文主要介绍了多维数组在C++语言中的定义、初始化、下标引用以及使用范围for语句处理多维数组的方法,具有一定的参考价值,感兴趣的可以了解一下... 目录多维数组多维数组的初始化多维数组的下标引用使用范围for语句处理多维数组指针和多维数组多维数组严格来说,C++语言没