我好好使用Promises吗?

时间:2017-01-27 12:41:56

标签: javascript json node.js promise file-writing

Probem

我的问题是我希望我的代码执行以下操作:

  1. 提出初步请求
  2. 一旦我收到了该请求的答案,我就会处理它并再次发出一批请求
  3. 完成批处理并完成所有响应后,我写了一个文件
  4. 文件完成后,我打印一条消息
  5. 我前两步是正确的,但是我的程序并没有像我预期的那样完成最后两步。

    代码

    此代码试图举例说明我想要实现的目标。仅使用promisejsonfile它是一个简单的应用程序,它详细地表示了我的代码的体系结构,并且只要您安装了两个库,它就可以实现。

    let jsonfile = require("jsonfile");
    let Promise = require("promise");
    let write = Promise.denodeify(jsonfile.writeFile);
    
    let writeOutput = function(filename, content) {
        return write(filename, content, {spaces: 4});
    };
    
    //Returns a random number each time it is invoked
    //after a random period of time between 1s and 6s
    let requestSimulator = function() {
        return new Promise((fulfil, reject) => {
            let randomNum = Math.random();
            let wait = Math.floor((Math.random() * 6000) + 2000);
            setTimeout(() => fulfil(randomNum), wait, randomNum);
        });
    };
    
    //Returns an array of rounded numbers
    let roundNumbers = function(someNumbers) {
        let numbersArr = [];
        let tmpNum;
        for (let number of someNumbers) {
            tmpNum = Math.floor(number);
            console.log("Rounded  " + number + " to " + tmpNum);
            numbersArr.push(tmpNum);
        }
    
        return numbersArr;
    };
    
    //Receives an array of rounded numbers, and for each number
    //makes a new request. 
    //It then sums the response with the given number.
    let sumNumbersBatch = function(numbersArr) {
        let promisesArray = [];
    
        for (let number of numbersArr) {
    
            let promise = new Promise((fulfil, reject) => {
                requestSimulator()
                    .then(result => {
                        let newNum = number + result;
                        console.log("Summing " + number + " with " + result + "resultint in " + newNum);
                        fulfil(newNum);
                    });
            });
    
            promisesArray.push(promise);
        }
    
        return new Promise.all(promisesArray);
    };
    
    //Starts the process
    let getData = function() {
        return new Promise((fulfil, reject) => {
            requestSimulator()
                .then(number => fulfil([number, number * 2, number * 3]));
        });
    };
    
    console.log("Starting program");
    getData()
        .then(roundNumbers)
        .then(sumNumbersBatch)
        .then(newNumbers => writeOutput("testFile.txt", newNumbers))
        .then(console.log("Program finished"))
        .catch(console.log); 
    

    运行之后,您可以看到输出类似于:

    Starting program
    Program finished
    Rounded  0.20890058801647582 to 0
    Rounded  0.41780117603295164 to 0
    Rounded  0.6267017640494275 to 0
    Summing 0 with 0.05537663551196226resultint in 0.05537663551196226
    Summing 0 with 0.34853429001859215resultint in 0.34853429001859215
    Summing 0 with 0.988336787994851resultint in 0.988336787994851
    

    哪个错!!!!

    Program finish应该出现在最后,而不是第二个!

    问题:

    所以现在我对我的代码有疑问:

    1. 我是否正确使用了Promise.all?
    2. 我能很好地宣传write功能吗?
    3. 此外,我愿意接受有关代码质量的建议!!!!

      非常感谢任何帮助和解释。

3 个答案:

答案 0 :(得分:4)

我已经重写了整个答案以匹配修改后的代码。我对“答案”的第一次尝试只不过是对第一个提供的代码看似错误的扩展评论;所以什么都没有丢失。

你的大部分代码都是正确的,这一行实际上是“错误的”:

.then(console.log("Program finished"))

并让您感到困惑,因为它会立即调用console.log("Program finished")并返回undefined,然后转换为.then(undefined)

应该是

.then(() => console.log("Program finished"))

new

前面应该有 Promise.all()

虽然可以改进一些事情,特别是你使用Deferred反模式。这是在没有必要时手动创建延迟对象,当你已经在那个地方处理承诺时。像这样:

//Starts the process
let getData = function() {
    return new Promise((fulfil, reject) => {
        requestSimulator()
            .then(number => fulfil([number, number * 2, number * 3]));
    });
};

更好的是

//Starts the process
let getData = function() {
    return requestSimulator().then(number => [number, number * 2, number * 3]);
};

而在requestSimulator中,您需要创建new Promise()才能将Promises与setTimeout()一起使用。这是合适的。

let jsonfile = require("jsonfile");
let Promise = require("promise");
let write = Promise.denodeify(jsonfile.writeFile);

//OK, now this function has a purpose/additional value (formatting)
//and is not just forwarding the arguments
let writeOutput = function(filename, content) {
    return write(filename, content, {spaces: 4});
};

//fine, a mock
let requestSimulator = function() {
    return new Promise((fulfil, reject) => {
        let randomNum = Math.random();
        let wait = Math.floor((Math.random() * 6000) + 2000);
        //there's no need/reason to pass `randomNum` to setTimeout as a third argument
        setTimeout(() => fulfil(randomNum), wait);
    });
};

//this can be shortened to, although it doesn't log anymore
let roundNumbers = function(someNumbers) {
    return someNumbers.map(Math.floor);
};

//Receives an array of rounded numbers, and for each number
//makes a new request. 
//It then sums the response with the given number.
let sumNumbersBatch = function(numbersArr) {
    //this again can be achieved simpler by using `Array#map` instead of `for..of`
    let promisesArray = numbersArr.map(number => {
        return requestSimulator()
            .then(result => result + number);
    });

    //no `new` here! Promise.all() is just a utility-function, no constructor.
    return Promise.all(promisesArray);
};

//Starts the process
let getData = function() {
    //removed the wrapping Promise.
    return requestSimulator()
        .then(number => [ number, number * 2, number * 3 ]);
};

console.log("Starting program");
getData()
    .then(roundNumbers)
    .then(sumNumbersBatch)
    .then(newNumbers => writeOutput("testFile.txt", newNumbers))
    //this executes `console.log()` immediately and passes the result (`undefined`) to `then()`
    //.then(console.log("Program finished"))
    //but `then()` needs a function:
    .then(() => console.log("Program finished"))
    //here you pass a reference to the function `console.log`, that's fine
    .catch(console.log); 

答案 1 :(得分:0)

  1. 我是否正确使用Promise.all
  2. 是和否,你使用Promise.all的方式很奇怪 - 你总是提供一个空数组。 Promise.all期望作为输入的一组承诺,它等待输入中的所有承诺被解析或者它们中的任何一个失败。 它返回一个已解决的promise(如果所有输入promise都可以)或被拒绝(如果失败)。在原始情况下,Promise.all始终被解析,因为输入列表为空

    1. 我的文件是在所有请求完成后写的,还是在完成后写的?
    2. 在从writeOutput返回的Promise之后调用的中的方法makeBatchRequests被解析,它被调用两个参数 - fileName,即{{1因为它从未在您的代码中定义,而第二个是undefined - 这是一个result,其成员的解析结果为Array,它始终为空。从技术上讲,是的,在所有请求完成后调用函数,但没有数据写入文件(哦,实际上,空字符串promisesArray是{{1}将被打印到文件:])

      1. 我能很好地宣传写作功能吗?
      2. ,你做得对。

        除此之外,尝试重新编写代码,一步一步,在继续执行每个步骤时对其进行测试,并将预期结果与您获得的结果进行比较。 如上面的答案所述,还有很多东西需要修复,祝你好运! :]

答案 2 :(得分:0)

那么,我认为问题可能在writeOutput(fileName, result)上,你确定它会返回一个承诺吗?

这是一个建议而非实际答案,但尝试这样做:

scrapy.getStanceMods()
    .then(['www.google.com', 'www.reddit.com'])
    .then(makeBatchRequest)
    .then(result => {
         return writeOutput(fileName, result))
            .then(console.log("Completed."))
            .catch(error=>console.log('will catch inner errors'))
    })
    .catch(error =>console.error(error));