gpt4 book ai didi

javascript - 在同步函数中使用 javascript `crypto.subtle`

转载 作者:行者123 更新时间:2023-11-30 19:19:31 26 4
gpt4 key购买 nike

在 javascript 中,是否可以在同步函数中使用浏览器内置的 sha256 哈希( https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest#Converting_a_digest_to_a_hex_string )?

理想情况下,我想做类似的事情

String.prototype.sha256 = function() {
// ...
return hash
}

我已经尝试过诸如 (async() => {hash = await digestMessage(message); return hash})() 之类的东西,但我只能取回 promise 对象。

在我看来,可能无法实现我想要的,但我想在放弃之前我会在这里问。谢谢!

最佳答案

TL; 博士
不,不可能在 JavaScript 中将异步函数包装在同步函数中并传播结果。请看这个excellent blog post on sync vs. async functions in various languages .要点是 JavaScript 是(许多语言中的)一种,其中异步函数由于语言运行方式的性质而具有传染性。
异步内置函数是 JS 的救星
JavaScript 在一个线程上运行。更具体地说,与特定网页相关的所有 JavaScript 都在同一线程上运行,以保证只有 一行 JS 将在任何特定时刻运行。这让我们尼安德特人的 Web 程序员无需负责编写互斥锁和原子操作等同步代码,以免多个线程同时写入同一内​​存并导致数据损坏甚至崩溃。
但是,我们只有一个线程来操作网页上的视觉元素并运行各种业务逻辑,例如加密/解密和数据管理,这有点糟糕。这一切都可能会变慢并损害用户体验。但是异步函数如何解决这个问题呢?拿这个功能:

function syncGenRSAKey() {
// fancy math stuff...

return generatedKey;
}
让我们让它异步(基于 promise ):
function asyncGenRSAKey() {
return new Promise((resolve, reject) => {
resolve(syncGenRSAKey());
});
}
希望你的直觉不会告诉你基于 Promise 的函数在这里更快。发生的一切是这样的:
  • 部分代码调用 asyncGenRSAKey()
  • 浏览器运行 Promise构造函数
  • Promise 构造函数立即/同步调用 (resolve, reject) => { ... }传递给它的回调函数
  • 浏览器运行 syncGenRSAKey()功能
  • promise 同步履行

  • 我们的代码仍然是完全同步的。我们一无所获。 记住,只有 一行我们的 JavaScript 将永远运行一次。只要我们底层的 key 生成代码( syncGenRSAKey() )是用 JavaScript 编写的,无论从哪里调用它,它都会在主线程上消耗时间。这意味着它将阻止浏览器跳转到其他 JavaScript,即事件处理程序。浏览器还会在主线程上渲染页面,因此它会卡住页面上的几乎所有内容(一些 CSS 动画被特别渲染),而 genRSAKey()在跑。用户可以悬停按钮,按钮背景和鼠标光标都不会更新。
    现在,请引用我回答的这一部分的副标题。关键字是内置的。内置函数,如 crypto.subtle 下提供的函数使用浏览器实现者选择的任何语言编写:C++、Rust 等。这些函数不是由 JavaScript 引擎运行的,它们是其中的一部分。它们可以产生尽可能多的操作系统线程,因为它们可以在您的计算机在给定时刻可以空闲的尽可能多(或尽可能少)的 CPU 内核上运行。这意味着 key 生成代码可以并且经常会与您的一堆 JavaScript 代码和页面渲染选项完全并行运行,然后浏览器将在 key 准备好并且任何当前正在运行的 JavaScript 完成运行时回调到您的 JavaScript ,触发要解决的 promise (如果生成 key 时出错,则拒绝),然后可以启动链接到生成 key 的任何 promise 中的代码。

    Now, is this really necessary for SHA-256 checksums in particular? No. In fact I myself still have a GitHub PR I've been putting off because I got tired of promisifying everything (which included some very complex Angular components) because I compute one f**king hash when the user opens a modal. This aside is for you, Suzanne.


    下面是两个很棒的视频,任何阅读这篇 StackOverflow 帖子的人都应该花时间观看。除非您充分了解 JavaScript 的同步/异步特性,能够准确地描绘出您的代码将如何运行,否则您并不真正了解 JavaScript,并且最终会遇到您无法理解的错误。
    The Node.js Event Loop: Not So Single Threaded
    Jake Archibald: In The Loop - JSConf.Asia async的澄清/ await在 JavaScript 中 asyncawait关键字是纯粹的语法糖。它们不能让你做任何你以前不能使用老式 promise 链做的事情,就像 promise 不能让你做任何你不能用好的 ole 嵌套回调函数做的事情一样。 async/ await只是让你的代码更简洁 10 倍。最后,与使用嵌套回调相比,promise 实际上会产生少量的运行时开销,因为 Promise 具有各种状态以方便将它们很好地链接起来并且是堆分配的; async/ await ,我听说,可以通过让 JS 引擎更容易地查看异步代码的整体上下文和使用变量的位置等,并进行优化来撤消这一小小的后退。
    以下是 async 的一些常见示例/ await正确使用。它们是用 TypeScript 编写的,以明确返回类型,但如果您只是去掉 : Whatever s 它变成了 JavaScript。
    在基于 Promise 的 API 中包装同步函数
    这实际上很少有必要,但有时您需要您的代码来适应第三方代码(如库)所需的接口(interface)。
    function withoutAsyncAwait(): Promise<number> {
    // Note that the reject callback provided to us by the Promise
    // constructor is rarely useful because the promise will
    // automatically be rejected if our callback throws an error,
    // e.g., if the Math.random() throws an error.
    return new Promise((resolve, reject) => resolve(Math.random()));

    // Could be (ignore the reject callback):
    // return new Promise(resolve => resolve(Math.random()));
    }

    async function withAsyncAwait(): Promise<number> {
    // If any synchronous code inside an async function throws an
    // error, a promise will still be returned by the async function,
    // but it will be rejected (by far the only desirable behavior).
    // The same is true if an await'ed promise rejects.
    return Math.random();
    }
    您不能(以及为什么要)避免 Promise构造函数,如果您要包装传统的基于回调的
    异步功能作为 promise 。
    function timeout(milliseconds: number): Promise<void> {
    return new Promise(resolve => window.setTimeout(resolve, milliseconds));
    }
    条件异步步骤
    有时您希望在一堆同步代码之前有条件地执行一个异步操作。前 async/ await这意味着您必须复制同步代码或将其全部包装在 promise 链中
    如果条件不成立,最初的 promise 将是一个空操作。
    function doStuffWithoutAsyncAwait1(needToMakeAsyncRequest: boolean): Promise<void> {
    // Might be a no-op promise if we don't need to make a request before sync code
    const promise = needToMakeAsyncRequest ? makeAsyncRequest() : Promise.resolve();

    return promise.then(() => {
    // tons of code omitted here, imagine like 30 lines...
    });
    }

    function doStuffWithoutAsyncAwait2(needToMakeAsyncRequest: boolean): Promise<void> {
    // Or we can just write the sync code twice, wrapping it in a promise in the branch
    // where we make an async request first. This sucks because our 30 lines of sync
    // code is written twice AND one of the times it is nested/indented inside of both
    // an if-statement and a .then() call
    if (needToMakeAsyncRequest) {
    return makeAsyncRequest().then(() => {
    // tons of code omitted here, imagine like 30 lines...
    });
    }

    // tons of code omitted here, imagine like 30 lines...
    }

    async function cmereAsyncAwaitYouSexyBoiYou(needToMakeAsyncRequest: boolean): Promise<void> {
    if (needToMakeAsyncRequest) {
    // Brings tears to my eyes 🥲
    await makeAsyncRequest();
    }

    // tons of code omitted here, imagine like 30 lines...
    }
    结合 async/await 和现有的 promise 机制 async/ await不是 Elixir 。它使编写一系列异步步骤变得非常干净
    但有时我们不只是想要一个序列:我们想要多个异步步骤同时运行。
    async function takes12SecondsTotal(): Promise<[string, string]> {
    const result1 = await takes7Seconds();
    const result2 = await takes5Seconds(); // will not get here till 1st result is done

    return [result1, result2];
    }

    async function takes7SecondsTotal(): Promise<[string, string]> {
    // Both inner functions start doing stuff immediately and we just wait for them
    // both to finish
    const [result1, result2] = await Promise.all([
    takes7Seconds(),
    takes5Seconds()
    ]);

    return [result1, result2];
    }

    function nottttttActuallyyyyyTheSammeeeeIKnowIKnowScrewErrorHandling(): Promise<[string, string]> {
    // We are almost there! However, we just introduced a potential sh!tstorm by reducing down our
    // code and getting rid of async/await: we now have the assumption that both the takes7Seconds()
    // and takes5Seconds() calls DO return promises... but they might have synchronous code and the
    // beginning of them that could throw an error because the author screwed up and then they will
    // blow up SYNCHRONOUSLY in our face and this function will also blow up SYNCHRONOUSLY and it
    // will continue up the call stack until it hits a try-catch or it reaches all the way out and
    // the JS engine stops it and logs it in the dev tools
    return Promise.all([
    takes7Seconds(),
    takes5Seconds()
    ]);

    // Let me illustrate:
    function takes5Seconds(): Promise<string> {
    const now = new Date; // Trivia: you don't need constructor parenthesis if no parameters

    if (now.getDay() === 6 && now.getHours() === 21) { // 9pm on a Saturday
    // Synchronous error
    throw Error("I ain't workin' right now, ok?")
    }

    // Returns a promise, whose rejection will be handled by the promise chain, so an
    // "asynchronous" error (but this function could also throw a synchronous error, you
    // never know)
    return doSomeWork();
    }
    }

    function thisIsFunctionallyTheSame(): Promise<[string, string]> {
    try {
    return Promise.all([
    takes7Seconds(),
    takes5Seconds()
    ]);
    } catch (err) {
    // catch any synchronous error and gift-wrap it in a promise to protect whoever calls
    // us from a synchronous error explosion
    return Promise.reject(err);
    }
    }

    async function justBeSmartAndUseAsync(): Promise<[string, string]> {
    // Even though we don't use await at all, async functions act as a stalwart line of defense,
    // stopping any synchronous errors thrown from continuing up the callstack, implicitly
    // catching them and making sure we return a promise NO MATTER WHAT (implicitly does what
    // I did above but the browser probably does it better since async functions are part of the
    // language spec and lots of work has been and will be put into optimizing them)
    return Promise.all([
    takes7Seconds(),
    takes5Seconds()
    ]);
    }
    我们甚至可能希望同时运行多个异步步骤序列。
    async function youCouldBeForgivenForDoingThis(): Promise<void> {
    // Please edit this answer if I'm wrong, but last time I checked, an await keyword holds up
    // the entire expression it's part of--in our case, that means the entire Promise.all(...)
    // expression. The doSomethingUnrelated() will not even start running until writeCode()
    // finishes
    await Promise.all([
    pushCodeToGitHub(await writeCode()),
    doSomethingUnrelated()
    ]);
    }

    async function armedWithEsotericJSKnowledge(): Promise<void> {
    // Also please note I just await the Promise.all to discard the array of undefined's and
    // return void from our async function
    await Promise.all([
    writeCode().then(code => pushCodeToGitHub(code)),
    doSomethingUnrelated()
    ]);
    }
    永远不要害怕将 promise 存储在变量中,或者混合使用 async箭头函数变成传统的 .then()根据需要获得最智能的代码的 promise 链。
    在异步函数中返回的深奥的 Bullsh*t
    如果你使用 TypeScript 或者对 JS Promise 很熟悉,你可能已经知道
    内部 .then()回调,你可以返回一个类型 T Promise<T>和 promise
    机制在内部完成工作以确保只是一个普通的 T被传递到下一个 .then()链上。 T可能是 number或任何其他类型的。 async函数做同样的事情
    事物。错误处理并没有那么简单。
    function getNumber(): number {
    return 420;
    }

    async function getNumberAsync(): Promise<number> {
    return getNumber(); // auto-wrap it in a promise cuz we're an async function
    }

    async function idkJavaScriptButIWantToMakeSureIGetThatNumber(): Promise<number> {
    return await getNumberAsync(); // this IS fine, really
    }

    async function iKNOWJavaScript(): Promise<number> {
    return getNumberAsync(); // this will NOT return Promise<Promise<number>> because async unwraps it
    }

    function iLikeToBlowUpRandomly(): Promise<number> {
    if (Math.random() > 0.5) {
    // This is not an async function so this throw clause will NOT get wrapped in a rejected promise
    // and returned pleasantly to the caller
    throw new Error("boom");
    }

    return getNumberAsync();
    }

    async function iHandleMyProblemsAndAlwaysFulfillMyPromises(): Promise<number> {
    try {
    return iLikeToBlowUpRandomly();
    } catch (err) {
    // This will always catch the "boom" explosions, BUT, if iLikeToBlowUpRandomly() returns a
    // rejected promise, it will sneakily slip through our try-catch because try-catches only
    // catch THROWN errors, and whoever called us will get a bad promise even though we
    // promised (haha) we would only ever return fulfilled promises containing numbers
    return -1;
    }
    }

    async function iActuallyHandleMyProblemsAndAlwaysFulfillMyPromises(): Promise<number> {
    try {
    // Bam! The normally extraneous await here brings this promise into our pseudo-synchronous
    // async/await code so if it was rejected, it will also trigger our catch branch just like
    // a synchronous error would
    return await iLikeToBlowUpRandomly();
    } catch (err) {
    return 3522047650; // call me if you have job offers 😉 but I'm kinda busy rn and spent way too much time on this
    }
    }

    关于javascript - 在同步函数中使用 javascript `crypto.subtle`,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57626477/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com