确保代码以正确的顺序执行JavaScript

时间:2016-12-13 11:59:51

标签: javascript asynchronous callback promise

我有一个生成电子邮件的javascript函数。

如果有电子邮件的附件,我会通过附件循环播放,每次上传一个附件到Dropbox而不是实际附加附件我附加到电子邮件正文的Dropbox链接到文件

(相关代码用反斜杠包装)

self.email = function (action, contact) {
    var emailTo = contact.email();
    var attachment = "";
    var lineBreak = "%0D%0A";
    var signature = uvm.user().fullName() 
    var body;
    var dropboxLink = "";
    switch (action) {
        case "a":
            body = "aaaa";
            break;
        case "b": case "c":
            body "bbbccc";
            break;
        //////////The piece of code that I'm working on//////////
        default:
            $.each(self.checkedDocs(), function (key, doc) {
                self.service.getDropboxLink(doc, function (result) {
                    dropboxLink = result;
                    attachment += lineBreak + doc.documentDescription() + ": " + dropboxLink; 
                });
            });
        body = "Please click on the link(s) below to view your document(s): "
        //////////End//////////
         }
    attachment = attachment ? attachment + lineBreak + lineBreak : lineBreak;
    body += lineBreak + attachment + signature;
    window.location.href = "mailto:" + emailTo + "?subject=" + self.subject() + "&body=" + body;
}

这是getDropboxLink函数:

self.getDropboxLink = function (doc, callback) {
    $.ajax({
        url: "/API/dropbox/DropboxUpload",
        type: "POST",
        data: ko.toJSON(doc),
        contentType: "application/json",
        success: function (data) {
            callback(data);
        }
    });
}

此代码无法正常运行,如果案例为default并且有电子邮件附件,则电子邮件之前 我从API获取Dropbox链接。

我可以将async=false添加到POST请求中,这样可以解决问题,但不推荐使用async=false

我认为我应该使用回调或承诺来执行此操作,但由于生成电子邮件的代码部分属于cases的所有switch和只在default部分调用API。

如果actiondefault我需要:

attachment = attachment ? attachment + lineBreak + lineBreak : lineBreak;
    body += lineBreak + attachment + signature;
    window.location.href = "mailto:" + emailTo + "?subject=" + self.subject() + "&body=" + body;

在完成此操作后发生:

$.each(self.checkedDocs(), function (key, doc) {
            self.service.getDropboxLink(doc, function (result) {
                dropboxLink = result;
                attachment += lineBreak + doc.documentDescription() + ": " + dropboxLink; 
            });
        });

如果操作为abc,则无关紧要。

我希望我能说清楚。有什么建议吗?

非常感谢任何帮助。

2 个答案:

答案 0 :(得分:1)

您可以通过计算仍需要获取的异步回复的数量来解决异步问题,并且只有在达到零时才会继续生成输出。如果将输出生成代码放在(嵌套)函数中,则可以同步调用它(对于非默认情况)和异步调用(对于默认情况,当计数器为零时):

self.email = function (action, contact) {
    var lineBreak = "%0D%0A";

    function openEmail(body, attachment) {
        attachment = attachment ? attachment + lineBreak + lineBreak : lineBreak;
        body += lineBreak + attachment + uvm.user().fullName();
        window.location.href = "mailto:" + contact.email() + "?subject=" + self.subject() + "&body=" + body;
    }

    switch (action) {
        case "a":
            openEmail("aaaa");
            break;
        case "b": case "c":
            openEmail("bbbccc");
            break;
        default:
            var attachment = "", 
                docs = self.checkedDocs(),
                count = docs.length;
            if (!count) { // treat the case where there are no attachments
                openEmail("No attachments");
                break;
            }
            $.each(docs, function (key, doc) {
                self.service.getDropboxLink(doc, function (dropboxLink) {
                    attachment += lineBreak + doc.documentDescription() + ": " + dropboxLink; 
                    if (--count == 0) { // last one:
                        openEmail("Please click on the link(s) below to view your document(s): ", attachment);
                    }
                });
            });
    }
}

承诺

您可以查看promises,它可以很好地处理异步任务。您将首先更改函数 getDropboxLink ,以便它返回$.ajax()调用的返回值,这是一个jQuery承诺。不再需要回调,也不需要成功处理程序。

然后在电子邮件函数中,您将在每个承诺上调用then方法以返回格式化的附件文本。 then方法也返回一个promise,所有这些promise都可以映射到一个数组中。使用$.when,您可以在完成所有这些承诺后获得信号。您在其回调中获得的参数将是您在then回调中返回的任何内容。

以下是进行这些调整的代码:

self.email = function (action, contact) {
    var lineBreak = "%0D%0A";

    function openEmail(body, attachment) {
        attachment = attachment ? attachment + lineBreak + lineBreak : lineBreak;
        body += lineBreak + attachment + uvm.user().fullName();
        window.location.href = "mailto:" + contact.email() + "?subject=" + self.subject() + "&body=" + body;
    }

    switch (action) {
        case "a":
            openEmail("aaaa");
            break;
        case "b": case "c":
            openEmail("bbbccc");
            break;
        default:
            var docs = self.checkedDocs();
            if (!docs.length) { // treat the case where there are no attachments
                openEmail("No attachments");
                break;
            }
            $.when.apply($, docs.map(function (doc) {
                return self.service.getDropboxLink(doc).then(function (dropboxLink) {
                    return lineBreak + doc.documentDescription() + ": " + dropboxLink;                 
                });
            })).done(function() {
                var attachment = [].join.call(arguments, '');
                openEmail("Please click on the link(s) below to view your document(s): ", attachment);
            });
    }
}

self.service.getDropboxLink = function (doc) {
    return $.ajax({
        url: "/API/dropbox/DropboxUpload",
        type: "POST",
        data: ko.toJSON(doc),
        contentType: "application/json"
    });
}

注意:您的代码同时引用了self.service.getDropboxLinkself.getDropboxLink(没有服务)。请根据您的实际情况调整。

答案 1 :(得分:0)

首先建议使用promise.all,但我将以旧方式解释。

window.location.href = xxx将在您获得Dropbox链接之前执行,因此首先您需要知道有多少异步调用,并且在每个完成的回调中,检查是否所有任务都已完成

var results = arr.map((x,index)=>{
  $.ajax(url,data,function(){
    results[index] = true
    if(results.every((x)=>x)){
      location.href=xxxx
    }
  })
  return false
})