代码之家  ›  专栏  ›  技术社区  ›  Bacon

异步、回调和OOP JavaScript:我该如何组织?

  •  3
  • Bacon  · 技术社区  · 12 年前

    我正在构建一个插件,它可以获取JSON中一组图像的信息,然后在一些对话框中显示它们以供选择。不幸的是,我的第一直觉很清楚地导致了比赛条件:

    var ImageDialog = function () {};
    ImageDialog.prototype.items = [];
    
    ImageDialog.prototype.fetch_images() {
        var parse_images = function(data) {
            // Magically parse these suckers.
            data = awesome_function(data);
            this.items = data;
        };
    
        magicalxhrclass.xhr.send({"url": 'someurl', "success": parse_images, "success_scope": this});
    }
    
    ImageDialog.prototype.render = function () {
        this.fetch_images();
        // XHR may or may not have finished yet...
        this.display_images();
        this.do_other_stuff();
    };
    
    var monkey = new ImageDialog();
    monkey.render();
    

    我想我可以通过改变 parse_images 回调以包括其余的渲染步骤。然而,这看起来不太正确。为什么 fetch_images 方法调用了一堆关于显示图像的东西?

    那么:我应该在这里做什么?

    我很确定延期会有所帮助,但遗憾的是:我需要在没有任何外部库的情况下写这篇文章(

    对其他代码气味的评论也会很好!

    3 回复  |  直到 12 年前
        1
  •  2
  •   hugomg    12 年前

    一般来说,您可以使用的基本思想是,当常规程序使用return语句时(意思是“我的函数已经完成了,现在做你的工作!”

    function fetch_images(callback){
       magicalXHR({
           success: function(data){
               parse_images(data);
               callback(whatever);
           }
       }
    }
    

    或者,如果parse_images本身是一个异步函数:

    parse_images(data, callback)
    

    现在,当您调用fetch_images时,代码将在进入回调后进行,而不是假设fetch_iimages将在返回时完成

    fetch_images(function(
       display_images()
    })
    

    通过使用回调,你可以很好地模仿传统程序的功能(事实上,这是一种形式和另一种形式之间相当机械的转换)。现在您将遇到的唯一问题是错误处理变得棘手,循环等语言功能无法很好地处理异步回调,并且回调往往嵌套在回调地狱中。如果回调开始变得过于复杂,我会研究使用一种Javascript方言,这种方言可以编译为延续传递风格的Javascrit(其中一些在运行时不需要额外的库即可工作)。

        2
  •  2
  •   Dagg Nabbit    12 年前

    这个怎么样?

    var ImageDialog = function () {
        this.items = []; // just in case you need it before the images are fetched
    };
    
    ImageDialog.prototype.fetch_images(callback) {
        var that = this;
        function parse_images (data) {
            // Magically parse these suckers.
            data = awesome_function(data);
            that.items = data;
            callback.apply(that);
        };
    
        magicalxhrclass.xhr.send({"url": 'someurl', "success": parse_images, "success_scope": this});
    }
    
    ImageDialog.prototype.render = function () {
        this.fetch_images(function(){
            this.display_images();
            this.do_other_stuff();
        });
    };
    
    var monkey = new ImageDialog();
    monkey.render();
    
        3
  •  0
  •   Bacon    12 年前

    这是一个关于该怎么做的想法。

    ImageDialog.prototype.fetch_images() {
        var parse_images = function(data) {
            // Magically parse these suckers.
            data = awesome_function(data);
            this.items = data;
            fetch_images.caller() // Unfortunately, this is nonstandard/not in the spec. :(
        };
    
        magicalxhrclass.xhr.send({"url": 'someurl', "success": parse_images, "success_scope": this});
    }
    
    ImageDialog.prototype.render = function () {
        if (this.items === []) {
            this.fetch_images()
            return;
        } else {
            this.display_images();
            this.do_other_stuff();
        };
    };
    

    通过这种方式,我不会将一些实现细节传递给fetch_images,并且我可以进行缓存以启动。我是否仍在努力逃避CPS,或者这是否明智?