如何转换为使用 Promises 和 Q 库

How to convert to using Promises and Q Library

我们有一个正在增长的应用程序,我们的客户端脚本需要重构,以使其更干净、更精简和更易于维护。我正在尝试使用一个小模块,利用 Q 库进行承诺链接。

如您所见,我需要将一些 return 值从初始函数传回 promise 链的其余部分。 有人可以帮助我理解我需要做些什么才能将第一个函数 return 正确地作为 Promise 吗?然后解释链?

这是我的起点:

var promise = new Q.Promise(generateMoveRequest)
    .then(function (id) {
       var woNum = 12345;
       return layoutInit(woNum, id, true);
    }).then(function (result) {
        if (result) {
          return moveRequestInit(result, true);
        } else { 
          throw new Error('Template not loaded');
        }
    }).catch(function (err) {
        console.log(err);
    });

生成移动请求:

generateMoveRequest: function () {
        $.ajax({
            method: "GET",
            url: window.contextPath + '/api/settings/getjson',
            data: {name: "the_name"},
            success: function (data) {
                if (data.length) {
                    var id = $.parseJSON(data).Parent;                                          
                    return id;
                }
            },
            error: function (xhr, textStatus, errorThrown) {
                console.log("xhr: ", xhr);
                return null;
            }
        });
    }

布局初始化:

layoutInit: function (num, id, appendLayout) {
    $.ajax({
            method: "GET",
            url: window.contextPath + '/some/url',
            data: {num: num, id: id},
            success: function (data) {
                return layoutInit.callback(data, appendLayout);
            },
            error: function (xhr, textStatus, errorThrown) {
                console.log("xhr: ", xhr);
            }
        });
    },
    callback: function (data, appendLayout) {
        if (data && data.toString().toLowerCase() !== "blank") {
            if (appendLayout) {
                $(data).insertBefore($("#detailsection"));
            } else {
                $("#detailsection").html(data);
            }                               
        } else {
            $("#detailsection").html('');
        }
    },

generateMoveRequest 函数将执行,但链不再继续。没有 .then() 执行并且 layoutInit 永远不会被调用。

我正在使用 Q 库,但一些示例似乎遗漏了如何 start/create 承诺或将初始函数转换为承诺。

谁能解释一下我这里的错误或提供一个干净的例子?

jQuery promises 的实现不遵循 A+ 标准,所以你最好使用 Q。

promise 必须接收一个带有 2 个参数的函数 - resolvereject - 它们也是函数(请记住 Javascript 是一种函数式语言)。成功时必须调用 resolve 函数,错误时必须调用 reject 函数。您传递给这些函数的任何内容都将在 then()catch() 回调中可用。

长话短说,像这样重写你的代码:

var generateMoveRequest = function (resolve, reject) {
  $.ajax({
    method: "GET",
    url: window.contextPath + '/api/settings/getjson',
    data: {name: "the_name"},
    success: function (data) {
      if (data.length) {
        var id = $.parseJSON(data).Parent;
        resolve(id);
      } else {
        reject({message: 'Data not received'})
      }
    },
    error: function (xhr, textStatus, errorThrown) {
      reject({message: errorThrown})
  }
  });
}

var promise = new Q.Promise(generateMoveRequest)
    .then(function (id) {
       var woNum = 12345;
       return layoutInit(woNum, id, true);
    }).then(function (result) {
        if (result) {
          return moveRequestInit(result, true);
        } else {
          throw new Error('Template not loaded');
        }
    }).catch(function (error) {
      console.log(error.message)
    });

稍后编辑 - 查看代码片段以了解 Promise 的工作原理:

// any number of params
function willPerformAnAsyncOperationAndReturnAPromise(firstParam, secondParam, thirdParam) {
  var promise = new Q.Promise(function (resolve, reject) { // always resolve & reject
    // perform an async operation, like waiting one second
    window.setTimeout(function () {
      if (firstParam !== 0) {
        // if the 1st param is not 0, it's a success
        resolve ({
          first: firstParam,
          second: secondParam,
          third: thirdParam
        })
      } else {
        reject({message: 'The first param must not be null'})
      }
    }, 1000)
  })

  return promise
}

willPerformAnAsyncOperationAndReturnAPromise(1, 2, 3)
  .then(function (data) {
    console.log(data)
  })
  .catch(function (error) {
    console.log(error)
  })

willPerformAnAsyncOperationAndReturnAPromise(0, 2, 3)
  .then(function (data) {
    console.log(data)
  })
  .catch(function (error) {
    console.log(error)
  })

一些问题:

  • 你的函数 generateMoveRequest 应该 return 一些东西:$.ajax return 是一个承诺(它有一个 then 方法),所以你可以 return 那;

  • 您不需要像 generateMoveRequest return 那样创建新的承诺。这样你就可以避免 promise constructor anti-pattern.

建议代码:

generateMoveRequest: function () {
    return $.ajax({
        method: "GET",
        url: window.contextPath + '/api/settings/getjson',
        data: {name: "the_name"},
    }).then(function (data) {
        if (data.length) {
            var id = $.parseJSON(data).Parent;                                          
            return id;
        }
    }).catch(function (xhr, textStatus, errorThrown) {
        console.log("xhr: ", xhr);
        return null;
    })
}

var promise = generateMoveRequest()
    .then(function (id) {
        var woNum = 12345;
        return layoutInit(woNum, id, true);
    }).then(function (result) {
        if (result) {
           return moveRequestInit(result, true);
        } else { 
           throw new Error('Template not loaded');
        }
    }).catch(function (err) {
        console.log(err);
    });