在Node JS

时间:2017-04-13 19:52:44

标签: javascript node.js asynchronous gulp promise

我已经完成了这项任务:

// default task, runs through all primary tasks
gulp.task("default", ["media", "scripts", "styles", "html"], function () {
    // notify that task is complete
    gulp.src("gulpfile.js")
        .pipe(plugins.gulpif(ran_tasks.length, plugins.notify({title: "Success!", message: ran_tasks.length + " task" + (ran_tasks.length > 1 ? "s" : "") + " complete! [" + ran_tasks.join(", ") + "]", onLast: true})));

    // trigger FTP task if FTP flag is passed
    if (plugins.argv.ftp) {
        config_module.config(gulp, plugins, settings);
        ftp_module.upload(gulp, plugins, settings, ran_tasks, on_error);
    }

    // reset ran_tasks array
    ran_tasks.length = 0;
});

除FTP位外,哪种方法效果很好。我需要config_module.config()完成才能触发ftp_module.upload()。我已经尝试使用回调设置promises和匿名函数,但这些解决方案都不起作用; FTP功能在配置之前保持激活状态。

如何让ftp_module.upload()函数在开始之前等待config_module.config()完成?

编辑:这是我尝试的承诺,但仍无法正常运行:

new Promise(function (resolve) {
    config_module.config(gulp, plugins, settings);
    resolve();
}).then(function () {
    ftp_module.upload(gulp, plugins, settings, ran_tasks, on_error);
});

编辑:我希望不必发布modules_config.config()代码,因为它很长,但我认为有必要继续前进:

module.exports = {
    // config task, generate configuration file for FTP & BrowserSync and prompt dev for input
    config(gulp, plugins, settings) {
        // generate settings.json and start other functions
        const generate_config = function (callback) {
            return plugins.fs.stat("./settings.json", function (err) {
                if (err !== null) {
                    const json_data =
                    `{
                        "ftp": {
                            "dev": {
                                "hostname": "",
                                "port":     "21",
                                "mode":     "ftp",
                                "username": "",
                                "password": "",
                                "path":     ""
                            },
                            "dist": {
                                "hostname": "",
                                "port":     "21",
                                "mode":     "ftp",
                                "username": "",
                                "password": "",
                                "path":     ""
                            }
                        },
                        "browsersync": {
                            "dev": {
                                "proxy":  "",
                                "port":   "",
                                "open":   "",
                                "notify": ""
                            },
                            "dist": {
                                "proxy":  "",
                                "port":   "",
                                "open":   "",
                                "notify": ""
                            }
                        }
                    }`;

                    plugins.fs.writeFile("./settings.json", json_data, "utf8", function () {
                        callback();
                    });
                } else if (typeof callback === "function") {
                    callback();
                }
            });
        };

        // configue JSON data
        const configure_json = function (namespace, options, env, callback) {
            const prompts = [];

            // construct the prompts
            Object.keys(options).forEach(option => {
                const properties = options[option];

                // construct the prompt
                const prompt     = {
                    name:    option,
                    message: namespace + " " + option + ": ",
                };

                // construct the prompt
                Object.keys(properties).forEach(property => {
                    prompt[property] = properties[property];
                });

                // put the prompt in the array
                prompts.push(prompt);
            });

            // prompt the user
            return gulp.src("./settings.json")
                .pipe(plugins.prompt.prompt(prompts, function (res) {
                    // open settings.json
                    const file = plugins.json.read("./settings.json");

                    // update data in JSON
                    Object.keys(options).forEach(option => {
                        file.set(namespace + "." + env + "." + option, res[option]);
                        settings[namespace][option] = res[option];
                    });

                    // write updated file contents
                    file.writeSync();

                    if (typeof callback === "function") {
                        callback();
                    }
                }));
        };

        return new Promise (function (resolve) {
            // get the target environment
            const env = plugins.argv.dist ? "dist" : "dev";

            // generate settings.json
            generate_config(function () {
                // read browsersync settings from settings.json
                settings.browsersync.proxy  = plugins.json.read("./settings.json").get("browsersync." + env + ".proxy");
                settings.browsersync.port   = plugins.json.read("./settings.json").get("browsersync." + env + ".port");
                settings.browsersync.open   = plugins.json.read("./settings.json").get("browsersync." + env + ".open");
                settings.browsersync.notify = plugins.json.read("./settings.json").get("browsersync." + env + ".notify");

                // read FTP settingss from settings.json
                settings.ftp.host = plugins.json.read("./settings.json").get("ftp." + env + ".hostname");
                settings.ftp.port = plugins.json.read("./settings.json").get("ftp." + env + ".port");
                settings.ftp.mode = plugins.json.read("./settings.json").get("ftp." + env + ".mode");
                settings.ftp.user = plugins.json.read("./settings.json").get("ftp." + env + ".username");
                settings.ftp.pass = plugins.json.read("./settings.json").get("ftp." + env + ".password");
                settings.ftp.path = plugins.json.read("./settings.json").get("ftp." + env + ".path");

                // configure FTP credentials
                configure_json("ftp", {
                    hostname: {
                        default: settings.ftp.host,
                        type:    "input",
                    },
                    port: {
                        default: settings.ftp.port,
                        type:    "input",
                    },
                    mode: {
                        default: settings.ftp.mode === "ftp" ? 0 : settings.ftp.mode === "tls" ? 1 : settings.ftp.mode === "sftp" ? 2 : 0,
                        type:    "list",
                        choices: ["ftp", "tls", "sftp"],
                    },
                    username: {
                        default: settings.ftp.user,
                        type:    "input",
                    },
                    password: {
                        default: settings.ftp.pass,
                        type:    "password",
                    },
                    path: {
                        default: settings.ftp.path,
                        type:    "input",
                    },
                }, env, function () {
                    // configure BrowserSync settings
                    configure_json("browsersync", {
                        proxy: {
                            default: settings.browsersync.proxy === "" ? "localhost" : settings.browsersync.proxy,
                            type: "input",
                        },
                        port: {
                            default: settings.browsersync.port === "" ? "8080" : settings.browsersync.port,
                            type: "input",
                        },
                        open: {
                            default: settings.browsersync.open === "" ? "external" : settings.browsersync.open,
                            type: "input",
                        },
                        notify: {
                            default: settings.browsersync.open === "" ? "false" : settings.browsersync.open,
                            type: "input",
                        },
                    }, env, function () {
                        // resolve the promise
                        resolve();
                    });
                });
            });
        });
    }
};

正如您所看到的,它正在返回一个承诺,但无论出于何种原因,我仍然无法在其后触发FTP任务。

4 个答案:

答案 0 :(得分:3)

您的问题已经有了一个可能的答案:承诺。

问题是你做错了。

在您在编辑中发布的代码(使用promises),您正在调用 config_module 方法(这似乎是异步的)然后您立即解决了承诺。在这种情况下,由于该方法是异步的,因此在完成 config 方法的处理之前会解析promise,从而导致不需要的行为。

正确的方法是你应该宣传 config_module 方法调用本身。这样,只有在完全执行方法后才能“解析”promise。

由于您没有发布其代码,因此很难说 config_module 方法应该如何。但是你应该在那里创建一个promise,然后只在计算完成后才解决它。所以你可以这样:

config_module.config(gulp, plugins, settings)
    .then(function () { 
        ftp_module.upload(gulp, plugins, settings, ran_tasks, on_error);
    });

编辑:

发布 config_module 代码后,更容易看到唯一缺少的是使用 config 方法返回的承诺来运行 ftp_module.upload .then 块内

答案 1 :(得分:0)

new Promise(function (resolve) {
    config_module.config(gulp, plugins, settings, ()=>{promise.resolve()});
}).then(function () {
    ftp_module.upload(gulp, plugins, settings, ran_tasks, on_error);
});

config_module.config = (gulp, plugins, settings, doneCallback){
  //...do stuff
  doneCallback()
}

一种方法,

另一种方法是你必须链接的函数返回promises本身,这样你可以使用像这样的函数链接它们:

var pipeline = (tasks, arg) => {
  return tasks.reduce((promise, fn) => {
    return promise.then(fn)
  }, Promise.resolve(arg))
}

基本上,当您的链接函数解析它的承诺时,它会使用您传递的数据调用下一个函数来解析它。

答案 2 :(得分:0)

您必须return承诺才能同步

但为此,您的函数必须返回一个承诺(例如config_module.configftp_module.upload。如果您没有承诺退回功能,则可以使用promisifycallback功能转换为promise

new Promise(function () {
    var _config = Promise.promisify(config_module.config);  //make your callback a Promise
    return _config(gulp, plugins, settings);    //now call the function, and return its result (which is a Promise now)
}).then(function () {
    var _upload = Promise.promisify(ftp_module.upload);
    return _upload(gulp, plugins, settings, ran_tasks, on_error);
});

答案 3 :(得分:0)

我建议你使用Async。它是一个功能强大的模块,有助于构建应用程序并使控制流程更容易。

Async提供的功能之一是series,它允许您逐个调用函数(即第二个函数在第一个函数完成之前不会运行)。

async.series(
   [
      function(callback) {
         // ...
         config_module.config();
         // do some more stuff if needed ...
         callback(null, 'one');
      },
      function(callback) {
         // ...
         ftp_module.upload();
         // do some more more stuff if needed ...
         callback(null, 'two');
      }
   ],
   // optional callback
   function(err, results) {
     // results is now equal to ['one', 'two']
   }
);