我不太了解这种异步性质

时间:2018-07-18 00:21:52

标签: javascript node.js

鉴于以下结果,我希望@app.route("/signin",methods=["GET","POST"]) def signin(): # LOG A USER IN #forget any user_id session.clear() if request.method == "GET": return render_template("login.html") else: user_name = request.form.get("username") # ensure username is provided if not request.form.get("username"): return "must provide username" # ensure password is provided elif not request.form.get("password"): return "must provide password" # query the database for the username rows = db.execute("SELECT * FROM users where username = :username",{"username":user_name}) # ensure username exists if len(rows) != 1 or not pwd_context.verify(request.form.get("password"),rows[0]["hash"]): return "Invalid username" # remember which user has logged in session["user_id"] = rows[0]["id"] 出现在workingbegin行之间。

  

规则:

是否可以在不更改end文件的情况下使其正常工作?

app.js

app.js

service.js

const service = require('./service');

console.log('*********** begin ***********');

(async () => {
    const results = await service.init();
})();

console.log('*********** end ***********');

结果:

exports.init = () => {
    return new Promise((reject, resolve) => {
        setTimeout(() => {
            console.log('working...');
        }, 2000);
    });
};

5 个答案:

答案 0 :(得分:5)

app.js正在同时调用console.log('begin')console.log('end') ,而working异步。我想您可以通过更改service.js来同时打印working 来进行更改,但这可能与您的用例不符。因此,不更改app.js是不可能的。

如果您想要这样的东西,可以在end之后将async 放入 await函数中:

console.log('*********** begin ***********');
(async () => {
  const results = await service.init();
  console.log('*********** end ***********');
})();

答案 1 :(得分:3)

  

有没有一种方法可以使它在不更改app.js文件的情况下工作?

在Javascript中,您不能使异步结果同步工作。做不到有诸如Promise和async/await之类的各种工具可帮助您使用异步操作进行编程,但是基本规则是异步结果始终将是异步的。之后,唯一需要等待运行的代码就是挂接到异步结果,并仅在收到异步结果现已完成的通知时才执行该代码。

async函数中,您可以使用await编写用于异步操作的“同步查找”代码,但它仅“查找”同步并且仅在该函数中适用,并非真正同步(请参阅下面的说明)。

  

我希望工作出现在开始和结束行之间。

通常的误解是async函数没有阻塞。它不会等到它内部的所有await操作都完成并完成后才返回。

它会同步运行,直到第一个await。在它获得第一个await的那一点上,它将一个promise返回给调用者,并且该函数调用之后的代码继续运行。

有时,当promise解析了等待的内容,并且此时没有其他Javascript在运行时,该函数将从中断处开始运行,并运行更多操作,直到下一个await或返回为止。如果找到另一个await,它将再次暂停执行并将控制权返回给JS解释器以运行其他事件(非阻塞)。如果它到达功能块的末尾或遇到return语句,则可以更早解决它返回的诺言。

因此,在您的这段代码中:

const service = require('./service');

console.log('*********** begin ***********');

(async () => {
    const results = await service.init();
})();

console.log('*********** end ***********');

这是事件的顺序:

  1. 同步加载服务模块。
  2. console.log('*********** begin ***********');
  3. 调用异步IIFE函数。
  4. 该异步功能一直运行到到达第一个await为止。 service.init()运行并返回一个承诺,await操作将等待。届时,该函数将返回一个单独的Promise(您没有使用或关注它)。所有async函数都返回一个诺言。
  5. 异步函数之后的代码现在运行,您得到console.log('*********** end ***********');
  6. 有时以后service.init()解析它返回的诺言,并且results变量中填充了该诺言的解析值。

虽然asyncawait很有用,但它们大多是语法糖,使编程更容易。可以将它们转换为使用.then()而不是await的常规promise处理。例如,假设您有以下内容:

async function foo1() {
    const results = await service.init();
    console.log("got results", results);
    return results;
}

foo1().then(results => {
    console.log("all done now");
}).catch(err => {
    console.log(err);
});

该foo函数也可以这样写:

function foo2() {
    try {
        return service.init().then(results => {
            console.log("got results", results);
            return results;
        });
     } catch(e) {
         return Promise.reject(e);
     }
}

foo2().then(results => {
    console.log("all done now");
}).catch(err => {
    console.log(err);
});

这两个实现的行为相同。 try/catch中的foo2()async函数自动执行的操作,仅在service.init()foo2()内的其他任何事物可能同步引发异常时才有用。

答案 2 :(得分:1)

Javascript是单线程,并利用所谓的事件循环来实现异步。

通过在Google上搜索event loop,您可以找到很多资源,我相信他们能比我用抽象概念所做的更好地解释它。 https://blog.carbonfive.com/2013/10/27/the-javascript-event-loop-explained/


所以,回到您在app.js中的代码

Node将立即运行这两行。

const service = require('./service');
console.log('*********** begin ***********');

此异步操作发生问题

(async () => {
    const results = await service.init();
})();

上面的日志记录后,立即处理了异步操作。 但是,它是async 而不是其内容,将立即进行解析。 内部的回调函数将被扔给相应的处理程序(您正在调用异步的节点函数,因此该处理程序将成为您的节点进程)。

当节点进程空闲时,它开始检查事件循环是否完成了任何操作。您对此无能为力,但是至少我们可以确定节点现在正忙,因为之后有一个同步操作,这是

console.log('*********** end ***********');

好吧,毕竟所有代码都已运行,并且节点进程现在变得空闲,他开始发现事件循环中是否有任何事情要做,他会发现有一个

const results = await service.init();

然后该节点将可以使用此功能,这就是为什么您看到

*********** begin ***********
*********** end ***********
working...

通过使用async / await,解决方案非常简单。 但是需要注意的是,您需要将要控制异步行为的所有进程放入同一个异步块中。

答案 3 :(得分:0)

也许这会有所帮助:

const service = require('./service');

console.log('*********** begin ***********');

(async () =>/*
             \
              \
               \
                \  Will be run in a later event loop cycle
                  ------------------------------------------------------> */ { const results = await service.init(); })();

console.log('*********** end ***********');

答案 4 :(得分:0)

简短的回答是否定的。您的匿名函数是异步的,因此它返回一个print,因此当您开始输出Promise时不会解决该问题。您可以将所有调用放在自己的异步函数中,然后等待结果,但这需要在异步函数上使用end

await

这将输出:

console.log('Top of script');

let closure = async () => {
  console.log('*********** begin ***********');

  let result =  await (async () => {
      const results = await service.init();
      return results;
  })();

  console.log('*********** end ***********');
  return result;
}

console.log('Promise not created yet')

let p = closure(); // creates promise

console.log('Promise created');

p.then(result => console.log('closure result:', result));

console.log('Bottom of script');

因此,您可以看到,当您调用Top of script Promise not created yet *********** begin *********** Promise created Bottom of script working... *********** end *********** closure result: Test result 时,异步函数中的代码开始运行,然后进行超时的异步调用,因此在执行“ begin”后,代码将继续执行等待,并且外部脚本继续说“已创建承诺”,并显示为“脚本底部”。

closure()调用告诉promise 完成后要做什么,因此,在完成超时之后,异步函数会在等待并记录“ end”后继续。当它返回完成承诺的结果并显示“关闭结果”时。

注意

p.then()相反,应该为(reject, resolve)。另外,您应该在超时时致电(resolve, reject),否则承诺将永远无法完成。我在my fiddle中使用了它:

resolve()