如何使用Promise,在函数完成之前完成ajax请求?

19

我有以下的函数来检查用户的会话以查看他们是否是员工。现在,我知道有更好的方法来做这件事,但我正在尝试制作一个与论坛软件相关联的简单应用程序。

function isStaff(callback) {
    $.ajax({
        url: url
    }).done(function(data) {
        var session = $.parseJSON(data);
        if (session.is_staff === 1) {
            callback(true);
        } else {
            callback(false);
        }

    });
}

假设我在编译“帖子”(Handlebars)时,使用这个函数就像这样:

Let's say I'm using this function in, like so, when compiling a "post" (Handlebars).
function compilePost(post) {
    var source = $('#feed-item-template').html();
    var template = Handlebars.compile(source);
    var context = {
        id: post.id,
        content: post.text,
        author: post.author,
        date: $.timeago(post.date),
        staff: function() {
            isStaff(function(response) {
                return response;
            });
        }
    }
    var html= template(context);
    return html;
}

问题在于检查用户是否是员工的请求在函数执行之后才完成,这会导致问题。

我知道 Promise 是 async: false 的替代方案,其中请求发出并在函数完成之前收到响应。

但我不知道如何将其转换为 Promise。我尝试学习,但我卡在概念上了。有人能向我解释一下吗?谢谢。

4个回答

25

首先,让我们简化compilePost函数。这个函数应该以同步的方式知道如何编译帖子。我们将isStaff获取更改为一个简单的参数。

function compilePost(post, isStaff) {
    var source = $('#feed-item-template').html();
    var template = Handlebars.compile(source);
    var context = {
        id: post.id,
        content: post.text,
        author: post.author,
        date: $.timeago(post.date),
        staff: isStaff
    }

    var html= template(context);
    return html;
}

现在,让我们创建一个新方法,只有一个目的 - 检查用户是否是员工成员:

function checkForStaffMemebership() {
    return new Promise(function (resolve, reject) {
        $.ajax({
            url: url,
            success: function (data) {
                var session = $.parseJSON(data);
                if (session.is_staff === 1) {
                    resolve(true);
                } else {
                    resolve(false);
                }
            }
        });
    });
}

这个函数将你原本的 ajax 调用包装成一个承诺。每当从服务器收到$.ajax 的响应时,承诺都会解决,无论用户是否为员工。

现在,我们可以编写另一个函数来协调这个过程:

function compilePostAsync(post) {
    return checkForStaffMemebership()
        .then(function (isStaff) {
            return compilePost(post, isStaff);
        });
}

compilePostAsync 函数判断用户是否为员工,然后编译博客。

请注意 compilePostAsync 返回一个 Promise,如果你之前有以下操作:

element.innerHTML = compilePost(post);

现在,你应该将它改成类似以下的内容:

compilePostAsync(post).then(function (compiledPost) {
    element.innerHTML = compiledPost;
});

注意:

  1. 这仅是一个示例,它可能会缺少一些东西(例如适当的错误处理)
  2. isStaffcheckForStaffMembership(原始和新的)都不接受任何参数,我猜你会想出如何传递userId或其他数据。
  3. 阅读有关Promises的内容,它是一个实用的工具,在网络上有很多关于它的数据,例如:MDN

现在,我们可以编写另一个函数来协调整个过程。我何时需要一个函数来“协调整个过程”?这对我来说很有趣。 - muser

4
根据文档,您无需使用已经实现了Promise的promise来包装ajax。相反,请按下面所述链接响应。

自jQuery 1.5起,由$.ajax()返回的jqXHR对象实现了Promise接口,使它们具有Promise的所有属性、方法和行为(有关更多信息,请参见Deferred对象)

您可以通过链接响应来执行以下操作:

function isStaff(url, post) {
    return $.ajax({
        url: url,
        dataType:"json"
    }).then(function(resp){
        //resp = $.parseJSON(resp); /*You dont require this if you have respose as JSON object. Just Specify it in 'dataType'*/
        var source = $('#feed-item-template').html();
        var template = Handlebars.compile(source);
        var context = {
            id: post.id,
            content: post.text,
            author: post.author,
            date: $.timeago(post.date),
            staff: resp.is_staff === 1 ? true : false
        };

        return template(context);
    });
}

isStaff(url, post).done(function(template){
    /*Your compiled template code is available here*/
}).fail(function(jqXHR, textStatus, errorThrown){
   console.log("Error:"+textStatus);
});

注意:一定要实现错误回调。因为你可能永远不知道出了什么问题 :)


使用$.defer解释Promise的简单说明:

为了理解,我创建了Fiddle,与您的要求类似。

解释:

基本上,Promise被引入以实现异步JS代码的同步执行。

什么是异步或异步代码?

执行的代码可能在任何给定时间返回一个值,这不是立即的。支持此语句的著名示例将是jquery ajax

为什么需要它?

Promise实现帮助开发人员实现依赖于响应的异步代码块的同步代码块,例如在ajax调用中,当我向服务器发出请求以请求数据字符串时,我需要等待服务器回复我响应数据字符串,我的同步代码使用它来操纵它,执行一些逻辑并更新UI。

请参阅链接,作者已用详细的示例进行了说明。

PS: Jquery的$.defer实现或包装了promise,但两者用途相同。


1
let basedataset = {}
let ajaxbase = {};
//setting api Urls
apiinterface();

function apiinterface() {
    ajaxbase.createuser = '/api/createuser'
}
//setting up payload for post method
basedataset.email = profile.getEmail()
basedataset.username = profile.getGivenName()
//setting up url for api 
ajaxbase.url = ajaxbase.createuser
ajaxbase.payload = basedataset;

//reusable promise based approach 
basepostmethod(ajaxbase).then(function(data) {
    console.log('common data', data);
}).catch(function(reason) {
    console.log('reason for rejection', reason)
});
//modular ajax (Post/GET) snippets
function basepostmethod(ajaxbase) {

    return new Promise(function(resolve, reject) {
        $.ajax({
            url: ajaxbase.url,
            method: 'post',
            dataType: 'json',
            data: ajaxbase.payload,
            success: function(data) {
                resolve(data);
            },
            error: function(xhr) {
                reject(xhr)
            }

        });
    });
}

0
使用async/await 在js中的解决方案如下:
async function getMyAjaxCall() {
  const someVariableName = await ajaxCallFunction();
}

function getMyAjaxCall() {
    return $.ajax({
    type: 'POST',
    url: `someURL`,
    headers: {
      'Accept':'application/json',
  },
    success: function(response) {
// in case you need something else done.
    }
    });
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接