我使用 Promises 好吗?

Am I using Promises well?

问题

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

  1. 提出初始请求
  2. 一旦我得到那个请求的答案,我就会处理它并再次发出一批请求
  3. 完成批处理并获得所有响应后,我将编写一个文件
  4. 文件完成后,我打印一条消息

前两步我做对了,但我的程序没有按预期执行后两步。

代码

这段代码试图举例说明我想要实现的目标。只有 promise and jsonfile 它是一个简单的应用程序,它详细地表示了我的代码的体系结构,并且它可以立即运行,前提是您安装了这两个库。

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 功能好吗?

此外,我愿意接受有关代码质量的建议!!!!

任何帮助和解释将不胜感激。

我重写了整个答案以匹配修改后的代码。我第一次尝试 "answer" 只不过是对第一个提供的代码似乎有问题的扩展评论;所以什么都没有丢失。

你的大部分代码都是正确的,这一行实际上是 "wrong":

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

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

应该是

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

而且Promise.all()

前面应该有没有new

尽管可以改进一些事情,尤其是您对延迟反模式的使用。当您已经在那个地方处理承诺时,这就是在不需要时手动创建延迟对象。喜欢这个:

//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. 我是否正确使用了 Promise.all

是也不是,您使用 Promise.all 的方式很奇怪 - 您总是提供一个空数组。 Promise.all 期望作为输入的承诺数组,它等待输入中的所有承诺得到解决或其中任何一个失败。 它 returns 一个要么被解决(如果所有输入的承诺都正确)要么被拒绝(如果任何一个失败)的承诺。在你原来的情况下 Promise.all 总是被解决,因为输入列表是空的

  1. 我的文件是真的在所有请求完成后写入,还是在它们完成时写入?

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

  1. 我承诺写功能好吗?

,你做对了。

除此之外,尝试重写您的代码,一步一步,在进行每一步时对其进行测试,并将预期结果与实际结果进行比较。 正如上面的答案中提到的,有很多东西需要修复,祝你好运! :]

那么,我认为问题可能出在 writeOutput(fileName, result),你确定 returns 是一个承诺吗?

这更像是一个建议,而不是实际答案,但请尝试这样做:

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));