将异步工作流更改为 Promise (Bluebird)

Change async workflow to Promise (Bluebird)

我一直在努力思考 Promises。对于我理解的基本概念,但是一旦嵌套,我就会有点困惑。感谢任何反馈

这是我试图重构到 Promises (bluebird) 中的代码

var getIndividualData = function(url, doneGetIndividualData) {
    var $, data;

    request(url, function(err, res, body) {
        if (!err && res.statusCode === 200) {
            $ = cheerio.load(body);

            data = {
                title: $("#itemTitle").children()["0"].next.data,
                condition: $("#vi-itm-cond").text(),
                price: $("#prcIsum_bidPrice").text(),
                imgUrl: $("#icImg")[0].attribs.src,
                createdAt: chance.date(),
                likes: chance.integer({min: 0, max: 1000})
            };

            doneGetIndividualData(null, data);
        } else {
            doneGetIndividualData(err);
        }
    });
};

var getListing = function(url, doneGetListing) {
    var $;
    var links = [];

    request(url, function(err, res, body) {
        if (!err && res.statusCode === 200) {
            $ = cheerio.load(body);

            $('.vip').each(function(i, el) {
                if (i < 15) {
                    links.push(el.attribs.href);
                }
            });

            async
                .concat(links, getIndividualData, function(err, result) {
                    return doneGetListing(null, result);
                });
        } else {
            doneGetListing(err);
        }
    });
};

var putToMongo = function(err, result) {
    if (devConfig.seedDB) {
        mongoose.connect(devConfig.mongo.uri);

        Item.find({}).remove(function(err, items) {
            Item.create(result, function(err, items) {
                console.log('done');
                process.kill();
            });
        });
    }
};

async
    .concat(urls, getListing, putToMongo);

要做的第一件事是将 request 包装在 returns 承诺中。许多 promise 库都有用于 "promisifying" 异步函数的实用程序,但我认为这在这里不起作用,因为 request 将两个成功值传递到其回调中:

var requestAsync = function(url) {
    return new Promise(function (resolve, reject) {
        request(function (err, res, body) {
            if (err) {
                reject(err);
            }
            resolve({ res: res, body: body});
        });
   });
};

一旦完成,就会变得容易得多:

var getIndividualData = function(url) {
    return requestAsync(url).then(function (result) {
        if (result.res.statusCode === 200) {
            var $ = cheerio.load(result.body);

            return {
                title: $("#itemTitle").children()["0"].next.data,
                condition: $("#vi-itm-cond").text(),
                price: $("#prcIsum_bidPrice").text(),
                imgUrl: $("#icImg")[0].attribs.src,
                createdAt: chance.date(),
                likes: chance.integer({min: 0, max: 1000})
            };
        }

        throw new Error("Individual data status code: " + result.res.statusCode);
    });
};

var getListing = function(url, doneGetListing) {
    return requestAsync(url).then(function (result) {
        if (result.res.statusCode === 200) {
            var $ = cheerio.load(result.body),
                promises = $('.vip').filter(function (i) { 
                    return i < 15;
                }).map(function (i, el) {
                    return getIndividualData(el.attribs.href);
                });

            return Promise.all(promises);
        }

        throw new Error("Listing status code: " + result.res.statusCode);
    });
};

var putToMongo = function(result) {
    if (devConfig.seedDB) {
        mongoose.connect(devConfig.mongo.uri);

        Item.find({}).remove(function(err, items) {
            Item.create(result, function(err, items) {
                console.log('done');
                process.kill();
            });
        });
    }
};

Promise.all(urls.map(getListing))
.then(putToMongo)
.catch(function (err) {
    // handle error
});