等待异步函数完成而不添加回调

Posted

技术标签:

【中文标题】等待异步函数完成而不添加回调【英文标题】:Wait for asynchronous function to finish without adding callback 【发布时间】:2013-08-11 14:41:56 【问题描述】:

我正在使用 Mocha 和 Should.js 为我的 Node.js/Express/Mongoose 项目编写测试,并且我正在测试访问我的 MongoDB 的函数。我希望这些测试完全独立于我数据库中的实际记录,所以我想创建一个条目然后加载它,并对它进行所有测试,然后删除它。我写了我的实际函数(我在整个项目完成后编写测试),这样create 函数没有回调;它只是在完成后呈现一个页面。在我的测试脚本中,我在调用create 之后调用了我的load_entry 函数,但有时create 需要的时间比平时长,因此load_entry 在它实际上无法加载文章时会抛出一个错误,因为它尚未创建.有什么方法可以确保异步函数在不使用回调的情况下完成?

如果我可以提供更多信息,请告诉我。我浏览了整个谷歌,找不到任何真正回答我问题的东西,因为大多数解决方案只是说“使用回调!”

【问题讨论】:

出于测试目的,请查看 sinon.js(可在 npm 上获得)。它允许您将异步调用(例如 ajax 调用和超时等)存根。与 mocha 配合使用效果很好。 【参考方案1】:

我找到了一个可行的解决方案。我所做的是向我的函数 (next) 添加一个回调,并且仅在指定时调用它(即,用于测试):

//more stuff above
article.save(function(err)
    if (!err) 
        console.log(req.user.username + ' wrote ' + article.slug)
        return next() || res.redirect('/admin')
    
    return next(err) || res.render('articles/entry_form', 
        title: 'New Entry',
        article: article,
    )
)

这样,当我运行实际的服务器并且没有指定回调时,它不会抛出错误,因为它只会返回res.render 语句。

【讨论】:

【参考方案2】:

使用所谓的promise

您可以阅读更多信息here

有很多很棒的库可以为您做到这一点。

Q.js 是我个人很喜欢的一种,现在被广泛使用。 Promise 也存在于 jQuery 中。

这是一个使用 q promise 和异步 json-p 调用的示例:DEMO

var time;
$.ajax(
    dataType: 'jsonp',
    type: 'GET',
    url: "http://www.timeapi.org/utc/now.json",
    success: function (data) 
        time = data;
    ,
    error: function (data) 
        console.log("failed");
    
)
.then(function() // use a promise library to make sure we synchronize off the jsonp
    console.log(time);    
);

【讨论】:

谢谢你!如果您想看看我最后做了什么,我在下面回答了我自己的问题,但这似乎是一个非常好的答案,可以在未来使用! :) 听起来不错,很高兴您有一个可行的解决方案!无论如何,我强烈建议您查看承诺。 Promise 是一个很好的模式。【参考方案3】:

由于执行异步操作的唯一本地方法是:setTimeoutsetIntervaladdEventListener,它们都接受回调,因此您最终将不得不在某处使用回调。

但是,您可以使用 Promises/A(也称为 Deferreds)来隐藏它。

您的代码可能如下所示:

db.create_entry("foo", data).done(function (entry) 
    db.delete_entry(entry).done(function () 
        console.log("entry is deleted");
    );
);

使用then-chaining:

db.create_entry("foo", data).then(function (entry) 
    return db.delete_entry(entry);
).done(function () 
    console.log("entry is deleted");
);;

【讨论】:

【参考方案4】:

这绝对是您想要回调的类型。除此之外,您将不得不编写某种回调包装器来轮询数据库以确定何时完成创建相关记录,然后发出事件或执行其他异步操作以允许测试继续。

【讨论】:

以上是关于等待异步函数完成而不添加回调的主要内容,如果未能解决你的问题,请参考以下文章

如何从同步方法调用中回调而不等待它?

等待先前的异步功能完成[重复]

使用异步而不等待

如何在.NET中调用异步函数而不等待它[重复]

如何让 Lambda 函数等待异步操作完成?

Windows 8 - 等待 Task<Bool> - 需要对已完成的侦听器进行异步回调