如何防止向所有函数添加 await/async?

问题描述 投票:0回答:3

从一个非常简单的例子开始,要求用户确认一个选择:

let a = function(){ return window.confirm( "Proceed" ); };
let b = function(){ console.log("b: " + a() ); };
let c = function(){ b(); console.log( "after b" ); };
c();
c();

window.confirm()
是一个异步事件,但该函数以同步方式运行并阻止执行,直到它已解决。

将此更改为使用自定义 UI,该 UI 使用

await
阻止执行,直到用户响应。但是
await
文档
指出:

await
运算符用于等待
Promise
。它只能在
async
函数中使用。

所以包含

await
表达式的函数必须做
async
并且返回一个promise,而不是之前的值,现在
a()
不会阻塞执行:

let a = async function(){
  let conf = document.createElement( "DIV" );
  conf.className = 'confirm';
  let message = document.createElement( "DIV" );
  message.className = 'confirm-message';
  message.innerText = 'Proceed';
  conf.appendChild( message );
  let ok = document.createElement( "BUTTON" );
  ok.innerText = 'Ok';
  conf.appendChild( ok );
  let cancel = document.createElement( "BUTTON" );
  cancel.innerText = 'Cancel';
  conf.appendChild( cancel );
  document.body.appendChild( conf );
  let v = await new Promise(
    response => {
      ok    .addEventListener( "click", ()=>{ response(true ); }, false );
      cancel.addEventListener( "click", ()=>{ response(false); }, false );
    }
  );
  conf.remove();
  return v;
};
let b = function(){ console.log("b: " + a() ); };
let c = function(){ b(); console.log( "after b" ); };
c();
c();
.confirm { display: grid; width: min-content; grid-template-columns: 10em 10em; grid-gap: 0.5em; grid-auto-rows: 1.4em; text-align: center; border: 1px #777 solid; background-color: #AAA; padding: 0.5em; }
.confirm > .confirm-message { grid-column: 1 / 3; }

这只是将问题移到调用堆栈上,后续函数需要使用

await
/
async
以确保它们等到用户输入已解决:

let a = async function(){
  let conf = document.createElement( "DIV" );
  conf.className = 'confirm';
  let message = document.createElement( "DIV" );
  message.className = 'confirm-message';
  message.innerText = 'Proceed';
  conf.appendChild( message );
  let ok = document.createElement( "BUTTON" );
  ok.innerText = 'Ok';
  conf.appendChild( ok );
  let cancel = document.createElement( "BUTTON" );
  cancel.innerText = 'Cancel';
  conf.appendChild( cancel );
  document.body.appendChild( conf );
  let v = await new Promise(
    response => {
      ok    .addEventListener( "click", ()=>{ response(true ); }, false );
      cancel.addEventListener( "click", ()=>{ response(false); }, false );
    }
  );
  conf.remove();
  return v;
};
let b = async function(){ let v = await a(); console.log("b: " + v ); };
let c = async function(){ let v = await b(); console.log( "after b" ); };
(async function(){
  let c1 = await c();
  let c2 = await c();
})();
.confirm { display: grid; width: min-content; grid-template-columns: 10em 10em; grid-gap: 0.5em; grid-auto-rows: 1.4em; text-align: center; border: 1px #777 solid; background-color: #AAA; padding: 0.5em; }
.confirm > .confirm-message { grid-column: 1 / 3; }

有没有办法使用新的

await
语法来阻止执行而不更改所有调用函数的签名并使它们成为
async
(模仿使用内置
window.confirm()
函数
的示例)?

javascript asynchronous
3个回答
0
投票

如果我理解正确,您想在顶级/全局上下文中阻止执行。 顶级 await 仅在模块中可用。除非你使用模块,你不能在全局上下文中阻止执行(即使那样,它也只会为那个模块被阻止)。

您可以做的下一个最好的事情是创建一种

main
异步函数,您可以在其中放置所有您希望阻止的代码。现在,如果您将
a()
b()
c()
放入此函数中,当使用
await
时,此函数中的所有其他代码都将被阻止。
main
函数将返回一个
Promise
并运行 async,但是,由于您的所有代码都在这个函数中,在它内部,它是同步的。让
main
函数阻塞整个 JavaScript 线程是不可能的。请注意,这需要重新编写您的函数才能使用
async/await
.

此解决方案涉及将问题尽可能多地移动到调用堆栈的顶部,如果您想阻止其他代码,这是唯一的解决方案。请注意,您也不能阻止页面上的其他视觉事件,例如聚焦输入、单击按钮、触发 CSS 悬停动画等,而不使用额外的 JS 在提示打开时取消这些事件或使用某种覆盖/背景。

这里有一些例子(我使用了箭头函数等 ES 特性,但如果这对你的项目不起作用,请随意更改):

(async () => {
    const a = () {
        // … render HTML etc.
        return await Promise(resolve => { /* … add event listeners */}
    }
    const v = await a();
    console.log("value: ", v);
    console.log("end");
})();

(async () => {…})()
是您的主要功能,您可以在其中模拟同步执行(如果您要阻止的所有代码都在该功能内)

如果你想使用

b()
c()
,同样你可以:

(async () => {
    const a = async () {
        // … render HTML etc.
        return await Promise(resolve => { /* … add event listeners */}
    }
    const b = async () {console.log("value: ", await a())};
    const c = async () {await b(); console.log("end");
    await c();
    // any other after this line is blocked until resolution of `c()`
})();

演示:

(async () => {
  const a = async () => {
    const conf = document.createElement('div');
    conf.className = 'confirm';
    
    const message = document.createElement('div');
    message.className = 'confirm-message';
    message.textContent = 'Proceed';
    conf.appendChild(message);
    
    const ok = document.createElement('button');
    ok.textContent = 'Ok';
    conf.appendChild(ok);
    
    const cancel = document.createElement('button');
    cancel.textContent = 'Cancel';
    conf.appendChild(cancel);
    document.body.appendChild(conf);
    
    const v = await new Promise(resolve => {
        ok.addEventListener('click', () => resolve(true), false);
        cancel.addEventListener('click', () => resolve(false), false);
    });
    conf.remove();
    return v;
  };
  const b = async () => console.log("b: " + await a());
  const c = async () => {
    await b();
    console.log( "after b" );
  };
  
  await c();
  // delay of 1 second
  await new Promise(resolve => setTimeout(resolve, 1000));
  console.log("2nd go");
  await c();
})();
.confirm { display: grid; width: min-content; grid-template-columns: 10em 10em; grid-gap: 0.5em; grid-auto-rows: 1.4em; text-align: center; border: 1px #777 solid; background-color: #AAA; padding: 0.5em; }
.confirm > .confirm-message { grid-column: 1 / 3; }

另一个选择当然是简单地使用一个module。像 Webpack 这样的第三方库通过将所有代码放在异步函数中来实现顶级等待,类似于我的演示。

总结:

无法阻止主线程或浏览器选项卡线程

在浏览器 JavaScript 中。那将是非常糟糕的,并且允许您“冻结”用户的选项卡,甚至不允许他们关闭它。模块中的顶级 await 可以进行阻塞。要模拟阻塞,您可以将所有代码放在异步上下文中;在这种情况下,任何

await
都会阻塞整个上下文。

在 Node.js 中,新版本支持顶级等待,您可以阻止 Node.js 应用程序的全局上下文。因此,如果您使用受支持的 Node.js 版本,则不需要添加像

(async () => {…})();
这样的包装器。


-1
投票

您应该使用

then()
功能。然后在履行承诺后返回异步函数的结果。

// First define your main async function. 
let a = async function(){
  let v = await new Promise(
    response => {
      ok    .addEventListener( "click", ()=>{ response(true ); }, false );
      cancel.addEventListener( "click", ()=>{ response(false); }, false );
    }
  );
 
  return v;
};

// Then get the results of your async function

let b = a.then((result) => { 
   console.log("b: " + result ); 
});


-1
投票

async
函数到达
await
语句时,函数的 caller 将继续执行,如果它不使用
await
也暂停执行。 这使您可以控制程序将在哪个级别停止并等待。

既然你想让它停在顶层,就无法避免在调用堆栈的所有函数中添加

await
/
async

© www.soinside.com 2019 - 2024. All rights reserved.