在 JavaScript 中,我/应该如何将 async/await 与 XMLHttpRequest 一起使用?
Posted
技术标签:
【中文标题】在 JavaScript 中,我/应该如何将 async/await 与 XMLHttpRequest 一起使用?【英文标题】:In JavaScript how do I/should I use async/await with XMLHttpRequest? 【发布时间】:2022-01-20 15:54:15 【问题描述】:完全披露:我认为自己具有中级 javascript 知识。所以这比我目前的经验水平略高。
我有一个 Google Chrome 扩展程序,它会在页面加载后立即对本地 file:///
执行 AJAX 请求。在我从请求中得到响应后,我稍后会在我的代码中的几个函数中使用返回的代码。大多数时候,我会在需要它的代码运行之前得到响应。但有时我不这样做,一切都会中断。
现在,我假设我可以将所有相关代码放入下面的 xhr.onload
中。但这似乎真的效率低下?我有很多依赖响应的活动部件,将它们全部放在那里似乎很糟糕。
我已经阅读了几篇与 async/await 相关的文章,但我无法理解这个概念。我也不是 100% 肯定我正在以正确的方式看待这个问题。我是否应该考虑使用 async/await?
这是我的 AJAX 请求的代码。
var xhr = new XMLHttpRequest();
xhr.open("GET", url, true);
xhr.onload = function(e)
code = xhr.response;
;
xhr.onerror = function ()
console.error("** An error occurred during the XMLHttpRequest");
;
xhr.send();
假设我有一堆函数需要稍后在我的代码中触发。现在它们看起来像:
function doTheThing(code)
// I hope the response is ready.
解决这个问题的最佳方法是什么?仅供参考,Fetch
API 不是一个选项。
这是我的代码结构的高级视图。
// AJAX request begins.
// ...
// A whole bunch of synchronous code that isn't dependant on
// the results of my AJAX request. (eg. Creating and appending
// some new DOM nodes, calculating some variables) I don't want
// to wait for the AJAX response when I could be building this stuff instead.
// ...
// Some synchronous code that is dependant on both my AJAX
// request and the previous synchronous code being complete.
// ...
// Some more synchronous code that needs the above line to
// be complete.
【问题讨论】:
您是否考虑过改用Fetch?它从一开始就是基于 Promise 的。 将代码放入回调中绝对不会影响效率或性能。它只是代码,回调只是回调。代码要么执行,要么不执行。 要将 XMLHttpRequest 与 async/await 一起使用,您需要做出承诺 只需从onload
函数内部调用doTheThing(code)
。
@E.Sundin Fetch 不适用于我需要的本地 file:///
文件。 @JaromandaX 这就是我的想法。但是很难让它发挥作用。
【参考方案1】:
我遇到了同样的问题,用下面的函数解决了:
const makeRequest = (method, url, data = ) =>
const xhr = new XMLHttpRequest();
return new Promise(resolve =>
xhr.open(method, url, true);
xhr.onload = () => resolve(
status: xhr.status,
response: xhr.responseText
);
xhr.onerror = () => resolve(
status: xhr.status,
response: xhr.responseText
);
if (method != 'GET') xhr.setRequestHeader('Content-Type', 'application/json');
data != ? xhr.send(JSON.stringify(data)) : xhr.send();
)
const test = async() =>
console.log("Starting request ...")
let request = await makeRequest("GET", "https://jsonplaceholder.typicode.com/todos/1");
console.log("status:", request.status)
console.log("response:", request.response)
test()
【讨论】:
【参考方案2】:我通常这样异步/等待:
async function doAjaxThings()
// await code here
let result = await makeRequest("GET", url);
// code below here will only execute when await makeRequest() finished loading
console.log(result);
document.addEventListener("DOMContentLoaded", function ()
doAjaxThings();
// create and manipulate your DOM here. doAjaxThings() will run asynchronously and not block your DOM rendering
document.createElement("...");
document.getElementById("...").addEventListener(...);
);
Promisified xhr function 这里:
function makeRequest(method, url)
return new Promise(function (resolve, reject)
let xhr = new XMLHttpRequest();
xhr.open(method, url);
xhr.onload = function ()
if (this.status >= 200 && this.status < 300)
resolve(xhr.response);
else
reject(
status: this.status,
statusText: xhr.statusText
);
;
xhr.onerror = function ()
reject(
status: this.status,
statusText: xhr.statusText
);
;
xhr.send();
);
【讨论】:
Uncaught SyntaxError: await is only valid in async function
就像它所说的那样,您需要将任何await
放在异步函数上。只需在 function
文本之前添加 async
。 async function doTheThing(code) let result = await makeRequest("GET", url); console.log(result);
感谢您的解释。我在上面编辑了我的问题,简要介绍了我的代码当前的结构。您还会基于此向我推荐您的答案吗?
我刚刚更新了我的示例。但是,如果您希望您的 2 个或更多独立任务并行(异步)运行,并且仅在所有任务完成后运行一些其他代码,请查看 Promise.all()。 developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
@ThắngTrầnXuân 如果我能每天都为你的答案投票,我会的!!!您刚刚结束了我多年来经历的最令人沮丧的开发会议!谢谢!谢谢!谢谢!【参考方案3】:
我为 XHR 创建了一个承诺。然后只需在 async
函数中使用 await
即可调用它。
function gethtml(url)
return new Promise(function (resolve, reject)
var xhr = new XMLHttpRequest();
xhr.open('get', url, true);
xhr.responseType = 'document';
xhr.onload = function ()
var status = xhr.status;
if (status == 200)
resolve(xhr.response.documentElement.innerHTML);
else
reject(status);
;
xhr.send();
);
async function schemaPageHandler()
try
var parser = new window.DOMParser();
var remoteCode = await getHTML('https://schema.org/docs/full.html');
var sourceDoc = parser.parseFromString(remoteCode, 'text/html');
var thingList = sourceDoc.getElementById("C.Thing");
document.getElementById("structured-data-types").appendChild(thingList);
catch(error)
console.log("Error fetching remote HTML: ", error);
【讨论】:
感谢您提供全面的示例。我不太确定 onload 方法中的 reject() 。可以肯定的是,如果您真的想处理所有错误,则应该有 onerror 处理程序。但是对于以 2xx 状态(OK)结尾的情况以外的情况是否触发 onload,我没有找到明确的答案。 There are tests saying that it does not happen,但另一方面在第一个示例中 in this docs, they do check status 为什么在这里使用 DOMParser?当您使用xhr.responseType = 'document';
时,xhr.response
已经是 HTMLDocument 类型。如果您改为使用resolve(xhr.response)
进行解析,那么您将直接获得您现在正在获得的sourceDoc
在我看来,现在您正在XHR 中进行DOM 解析,然后对结果进行字符串化(innerHTML)并再次进行DOM 解析。还是我错过了什么?【参考方案4】:
例如,您可以创建一个异步类来代替原来的类。它缺少一些方法,但可以作为一个例子。
(function()
"use strict";
var xhr = Symbol();
class XMLHttpRequestAsync
constructor()
this[xhr] = new XMLHttpRequest();
open(method, url, username, password)
this[xhr].open(method, url, true, username, password);
send(data)
var sxhr = this[xhr];
return new Promise(function(resolve, reject)
var errorCallback;
var loadCallback;
function cleanup()
sxhr.removeEventListener("load", loadCallback);
sxhr.removeEventListener("error", errorCallback);
errorCallback = function(err)
cleanup();
reject(err);
;
loadCallback = function()
resolve(xhr.response);
;
sxhr.addEventListener("load", loadCallback);
sxhr.addEventListener("error", errorCallback);
sxhr.addEventListener("load", function load()
sxhr.removeEventListener("load", load);
resolve(sxhr.response);
);
sxhr.send(data);
);
set responseType(value)
this[xhr].responseType = value;
setRequestHeader(header, value)
this[xhr].setRequestHeader(header, value);
addEventListener("load", async function main()
removeEventListener("load", main);
var xhra = new XMLHttpRequestAsync();
xhra.responseType = "json";
xhra.open("GET", "appserver/main.php/" + window.location.hash.substring(1));
console.log(await xhra.send(null));
);
());
【讨论】:
【参考方案5】:你有两个选择,
首先是使用基于promise的较新的fetch
api,你可以这样做
let response = await fetch(url);
response = await response.json();; // or text etc..
// do what you wanna do with response
如果你真的想使用 XMLHttpRequest 的其他选择是承诺它
let response = await new Promise(resolve =>
var xhr = new XMLHttpRequest();
xhr.open("GET", url, true);
xhr.onload = function(e)
resolve(xhr.response);
;
xhr.onerror = function ()
resolve(undefined);
console.error("** An error occurred during the XMLHttpRequest");
;
xhr.send();
)
// do what you wanna do with response
可能的完整解决方案
(async () =>
let response = await new Promise(resolve =>
var xhr = new XMLHttpRequest();
xhr.open("GET", url, true);
xhr.onload = function(e)
resolve(xhr.response);
;
xhr.onerror = function ()
resolve(undefined);
console.error("** An error occurred during the XMLHttpRequest");
;
xhr.send();
)
doTheThing(response)
)()
【讨论】:
感谢您的回答。不幸的是,fetch
不允许 file:///
这是我需要的。我会试试你的XMLHttpRequest
选项,看看是否适合我。
我收到此错误Uncaught SyntaxError: await is only valid in async function
@jkupczak 您需要在异步函数中包含这些位,例如:async function fn ()
,如果您在顶层执行此操作,您可以将代码放入异步 IIFE (async function fn() )()
跨度>
当你说// do what you wanna do with response
时,我该怎么做?稍后我有一个功能,我只想在响应返回后触发。我不知道如何编码。
@jkupczak 当我写// do what you wanna do with response
时,响应已经存在,您可以在那里调用您的函数,例如,将// do what you wanna do with response
替换为doTheThing(response)
以上是关于在 JavaScript 中,我/应该如何将 async/await 与 XMLHttpRequest 一起使用?的主要内容,如果未能解决你的问题,请参考以下文章
将数组从 Flash (AS3) 发送到 JavaScript
我应该如何将 javascript 函数调用到 kohana 视图中?