使用顺序承诺在Javascript和jQuery

时间:2018-02-04 12:40:00

标签: javascript jquery ajax

我有步骤数组:

var stepsToDo = [step1, step2, step3,...]

每一步都进行ajax调用:

function step1() { return $.ajax({ ... }); }
function step2() { return $.ajax({ ... }); }

step 1step 2和其他人的类似ajax调用示例):

return $.ajax({
            url: "test.php",
            type: "POST",
            dataType: "json",
            data: {"step-1": ""},
            beforeSend: function () {
                writeResult("Processing-> test 1");
            },
            success: function (result) {
                if (result) {
                    if (result instanceof Array) {
                        writeResult("Found: " + result.length + " items");
                        arr = result;
                    } else {
                        writeResult(result);
                        arr = [];
                    }
                }
            }
        });

function writeResult(str)  { console.log(str); }

我想按顺序执行(在stepsToDo中定义)。

我试过了:

stepsToDo.reduce(
    (promise, method) => {
        return promise.then(method);
    },
    Promise.resolve()
);

没有任何反应,控制台中没有打印。

为什么?

4 个答案:

答案 0 :(得分:1)

放弃new Promise。您的步骤是返回承诺的函数,而不是promise executors - 它们永远不会调用传递给它们的resolve回调。

答案 1 :(得分:0)

对于您最初创建的reducer功能......

stepsToDo.reduce((promise, method) => {
    return promise.then(_ => new Promise(method));
}, Promise.resolve());

...你的步骤函数应该resolvereject在reducer中创建的promise - 因此应该实现promise执行者签名,I.E:

type executor = (resolve: (value?: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => void;

例如:

function step1(resolve, reject) {
    $
        .ajax({ /* ... */ })
        .done(resolve)
        .fail(reject);
}

修改

正如@Bergi在评论中所述,这是is an anti-pattern。相反,您可以将Promise构造移动到步骤函数中。一个基本的例子(jQuery ajax API仍然必须转换为promises)

function step1() {
    return new Promise((resolve, reject) => {
        $.ajax({ /* ... */ }).done(resolve).fail(reject);
    });
}

如果您的所有步骤都返回了您的承诺,您的减速器可以变得更简单:

stepsToDo.reduce((promise, method) => promise.then(method), Promise.resolve());

在这种情况下,您甚至可以返回$.ajax的结果,因为jQuery promises会实现 then 方法:

function step1() {
    return $.ajax({ /* ... */ });
}

如果错误处理需要本机承诺,即Promise.catch,您可以使用Promise.resolve显式地将jQuery承诺强制转换为本机承诺:

function step1() {
    return Promise.resolve($.ajax({ /* ... */ }));
}

答案 2 :(得分:0)

我不确定其他答案有什么问题,但$ .get(或ajax或post)会返回一个承诺。

所以你的步骤方法可能如下所示:

var stepOne = () => {
  return $.ajax({//just return the promise like
    url: "test.php",
    type: "POST",
    dataType: "json",
    data: {"step-1": ""}
  });
}

然后,您可以将其减少为一个通过三个步骤的结果解决的承诺:

[stepOne,stepTwo,stepThree].reduce(
  (p,fn)=>
    p.then(
      results=>fn().then(result=>results.concat([result]))
    ),
  $.Deferred().resolve([])
)
.then(
  results=>console.log("got results:",results),
  err=>console.warn("Something went wrong:",err)
);

你看我没有通过Promise.resolve作为第二个参数来减少$.Deferred().resolve([]),这是jQuery承诺的价值。您现在可以支持不具备本机承诺的浏览器,而无需进行填充。

虽然如果您需要支持那些我建议不使用箭头功能语法并使用function(something){return something}代替something=>something

答案 3 :(得分:0)

原始问题或实施中缺少大部分内容。如果所有步骤函数都返回$.ajax承诺,则Array.reduce模式应该有效。

以下是使用step()函数模拟异步代码执行并使用完全相同的Array.reduce模式的概念的工作证明:

&#13;
&#13;
// Step generator
function step(number) {
  return function() {
    return $.Deferred(function(def) {
      setTimeout(function() {
        console.log('Running Step ', number);
        def.resolve();
      }, Math.floor(Math.random() * Math.floor(250)));
    }).promise();
  }
}

// List of steps
var stepsToDo = [step(1), step(2), step(3), step(4), step(5)];

// Consume steps one at a time
stepsToDo.reduce(
  (promise, method) => {
    return promise.then(method);
  },
  Promise.resolve()
).then(() => console.log('All Done!'));
&#13;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
&#13;
&#13;
&#13;