我怎样才能 运行 按顺序执行多项功能,并在其中任何一个失败时停止所有功能?

How can I run multiple functions sequentially, and stop all when any of them fail?

我有多个 Javascript 函数,每个函数都执行一些 DOM 操作,然后 运行 发出一个 ajax 请求。我希望能够 运行 第一个函数,它操纵 DOM 然后触发它的 ajax 请求,然后,当 ajax 请求完成时,我想 运行 第二个函数,如果 ajax 请求返回 true,或者停止执行其余函数并做一些其他 DOM 操作(比如显示错误消息)。

我希望这些函数按顺序 运行 一个接一个地运行,并且只有在 none 的 returns 请求 ajax 为 false 时才继续运行。如果其中 none 个 returns 为假,那么它们都应该 运行 并且最终我会在我的 "always" 回调中进行一些操作。

我怎样才能做到这一点?

我的第一个想法是使用 promises,让代码更简洁,但在阅读了几个小时之后,我就是不知道如何让它工作。

下面是我当前的代码,这是我在控制台执行时得到的:

inside test1
inside test2
inside test3
fail
[arguments variable from fail method]
always
[arguments variable from always method]

这是我想在我的控制台中获得的内容(注意缺少的 "inside test3" 字符串):

inside test1
inside test2
fail
[arguments variable from fail method]
always
[arguments variable from always method]

代码如下:

(function($) {
    var tasks = {
        init: function() {
            $.when(
                this.test1(),
                this.test2(),
                this.test3()
            ).done(function() {
                console.log('done');
                console.log(arguments);
            })
            .fail(function() {
                console.log('fail');
                console.log(arguments);
            })
            .always(function() {
                console.log('always');
                console.log(arguments);
            });
        },

        test1: function() {
            console.log('inside test1');
            return $.getJSON('https://baconipsum.com/api/?type=meat-and-filler');
        },

        test2: function() {
            console.log('inside test2');
            // note the misspelled "typ" arg to make it fail and stop execution of test3()
            return $.getJSON('https://baconipsum.com/api/?typ=meat-and-filler');
        },

        test3: function() {
            console.log('inside test3');
            return $.getJSON('https://baconipsum.com/api/?type=meat-and-filler');
        }
    };

    tasks.init();
})(jQuery)

有什么想法吗?

您不需要使用 promises - 也许您知道这一点,但您可以在前一个成功块中继续调用下一个 ajax 请求,如下所示:

(function($) {
    var tasks = {
        init: function() {
                    $.post(url_1, data_1, function(data,status){
            if(status == 'success')
            {
                $.post(url_2, data_2, function(data,status){
                    if(status == 'success')
                    {
                        // ... and the next Ajax request goes here, etc.
                    }
                    else {
                        // show error message if 2nd Ajax request fails
                    }
                }
            else {
                // show error message if 1st Ajax request failes
            }
        });
    });    
        }

    tasks.init();
})(jQuery)

我不太熟悉 jquery 承诺。

但这应该有效

(function ($) {
    var tasks = {
        init: function () {
            this.test1().done(this.test2).done(this.test3)
                .done(function () {
                    console.log('done');
                    console.log(arguments);
                })
                .fail(function () {
                    console.log('fail');
                    console.log(arguments);
                })
                .always(function () {
                    console.log('always');
                    console.log(arguments);
                });
        },

        test1: function () {
            console.log('inside test1');
            return $.getJSON('https://baconipsum.com/api/?type=meat-and-filler');
        },

        test2: function () {
            console.log('inside test2');
            // note the misspelled "typ" arg to make it fail and stop execution of test3()
            return $.getJSON('https://baconipsum.com/api/?typ=meat-and-filler');
        },

        test3: function () {
            console.log('inside test3');
            return $.getJSON('https://baconipsum.com/api/?type=meat-and-filler');
        }
    };

    tasks.init();
})(jQuery)

使用 promises 确实使这段代码更清晰

注意:Promise/A+ promise .then 和 .catch 回调只接受一个参数,所以不需要查看 arguments,只需使用一个参数

这段代码 (ES2015+) 展示了多么简单

let p = Promise.resolve();
Promise.all([this.test1, this.test2, this.test3].map(fn => p = p.then(fn()))).then(allResults =>

或者,使用 array.reduce

[this.fn1, this.fn2, this.fn3].reduce((promise, fn) => promise.then(results => fn().then(result => results.concat(result))), Promise.resolve([])).then(allResults =>

在这两种情况下,allResults 都是来自 testN 函数的(解析的)结果数组

它为 "start" 承诺链

创建了一个(已解决的)承诺

Array#map 链接每个函数(this.test1 等)以在先前结果的 .then 中执行。每个 this.testn 承诺的结果都在一个新数组中返回,这是 Promise.all

的参数

如果testN中有一个失败,则不会执行下一个

var tasks = {
    init () {
        let p = Promise.resolve();
        Promise.all([this.test1, this.test2, this.test3].map(fn => p = p.then(fn())))
        .then(results => {
            console.log('done');
            console.log(results);
            return results; // pass results to next .then
        }).catch(reason => {
            console.log('fail');
            console.log(reason);
            return reason; // because I return rather than throw (or return a Promise.reject), 
            //the next .then can will get `reason` in it's argument
        }).then(result => {
            console.log('always');
            console.log(result);
        });
    },
    test1() {
        return $.getJSON('https://baconipsum.com/api/?type=meat-and-filler');
    },
    test2() {
        return $.getJSON('https://baconipsum.com/api/?typ=meat-and-filler');
    },
    test3() {
        return $.getJSON('https://baconipsum.com/api/?type=meat-and-filler').then(result => {
            if(someCondition) {
                throw new Error("sum ting wong");
                // or
                return Promise.reject(new Error("sum ting wong"));
            }
            return result;
        });
    }
};
tasks.init();

题中的代码,你可以再简化一下

var tasks = {
    init () {
        let p = Promise.resolve();
        const urls = [
            'https://baconipsum.com/api/?type=meat-and-filler',
            'https://baconipsum.com/api/?typ=meat-and-filler',
            'https://baconipsum.com/api/?type=meat-and-filler'
        ];
        Promise.all(urls.map(url => p = p.then(() => $.getJSON(url))))
        .then(results => {
            console.log('done');
            console.log(results);
            return results; // pass results to next .then
        }).catch(reason => {
            console.log('fail');
            console.log(reason);
            return reason; // because I return rather than throw (or return a Promise.reject), 
            //the next .then can will get `reason` in it's argument
        }).then(result => {
            console.log('always');
            console.log(result);
        });
    }
};
tasks.init();