Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
126 views
in Technique[技术] by (71.8m points)

javascript - Promise.All - Promise Within Promise Not Being Resolved

Hello I am continuing to learn promises and am struggling with how to correctly use Promise.All or whether or not I should be using it in this situation.

I am connecting to a data source and resolving my first promise to an array. After that I want to take those results and with another promise loop through the results and add another value to the array which is using an external API call.

After research it sounded like I needed to use Promise.All so that the promises would be completed before returning my response but I am still getting a status of pending on the value that I am adding to my array instead of the expected value. Again I am just learning node and promises so my approach might be completely flawed and in that case I am open to any suggestions on a better way to do what I am attempting to accomplish.

function GetSearches(req, res) { 

    var BingSearches = new Promise((resolve, reject) => {
        GetBingSearches().toArray((err, response)=>{
            resolve(response);
        });          
    }) /* get data from external data source example data: [{SearchTerm: "Boxing", id: '4c142f92-ba6d-46af-8dba-cb88ebabb94a', CreateDate: '2017-08-10T13:59:20.534-04:00'}] */


    var GetSearchesWithImageUrl = BingSearches.then((response,reject) =>{
        for(var i in response){
            response[i].Image = new Promise((resolve, reject) => { 
                Bing.images(response[i].SearchTerm, {
                count: 1
                }, (err,res,body) => {
                    const bingImageUrl = body.value[0].contentUrl;
                    console.log(bingImageUrl) // bing image url link
                    resolve(bingImageUrl);
                });
            });
        } // loop through array and add value

        return response;
    }).catch((err) => {
        console.log(err);
    })

    return Promise.all([BingSearches ,GetSearchesWithImageUrl ]).then(([BingSearches , GetSearchesWithImageUrl ]) => {
        console.log(GetSearchesWithImageUrl )
         /* my response is now [{SearchTerm: "Boxing", id: '4c142f92-ba6d-46af-8dba-cb88ebabb94a', CreateDate: '2017-08-10T13:59:20.534-04:00', Image: Promise { pending } }] */

        res.status(200).json(GetSearchesWithImageUrl )
    }).catch((err) => {
        console.log(err);
    })
}

I was hoping that Promise.All would wait for all my promises to complete but I do not seem to understand how it works. I am open to any advice/explanation of what I am doing wrong.

Thanks!

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

Here's my best stab at this problem:

// Promisified search, resolves to response array
function getBingSearchesPromise() {
    return new Promise((resolve, reject) => {
        GetBingSearches().toArray((err, response) => {
            if (err) {
                reject(err);
            } else {
                resolve(response);
            }
        });          
    });
}

// get first image url for a search term, resolves to an image URL
function getBingImagePromise(term) {
    return new Promise((resolve, reject) => {
        Bing.images(term, {count: 1}, (err, res, body) => {
            if (err) {
                reject(err);
            } else {
                resolve(body.value[0].contentUrl);
            }
        });
    });
}


// no need to pass req or res into here
// returns promise, resolves to array of images URLs
function GetSearches() {
    return getBingSearchesPromise().then(searchResponses => {
        // get all images
        let promises = searchResponses.map(searchItem => {
            return getBingImagePromise(searchItem.SearchTerm);
        });
        return Promise.all(promises);
    })    
}

// then inside your request handler where you have req and res, you can do this
GetSearches().then(images => {
    res.json(images);
}).catch(err => {
    console.log(err);
    res.status(500).end();
});

Because your code was pretty far off and did not include a words description of what you are trying to do, this is an educated guess about you're trying to accomplish. If I got the objective wrong with my guess, you can either just learn from this and apply it to your actual problem or I can delete my answer.

Summary of Changes:

  1. Promisify async operations separately, then build all logic with promises (no plain callbacks). This could probably be done even better if we could see the code for GetBingSearches() which should probably have some arguments passed into it rather than be hard-wired to do a specific type of search.

  2. Get the searches promise and wait for it with .then() to get an array of results.

  3. In that .then() handler, use .map() to fetch each image URL and create an array of promises.

  4. Wait for that array of promises with Promise.all().

  5. Separate out req and res from this code because only the final response needs to use res so this way you code can be more usable and you can just call this code from the response handler and get the result without passing res into it.

  6. Add error handling for all errors. Propagate all errors back up the chain.

  7. Never use for/in to iterate an array (it iterates properties of an object, not just array elements). If your desired result is a 1-to-1 array, then .map() is perfect for that. Otherwise, in ES6, use for/of to iterate an array.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...