在这种情况下如何正确使用 $.Deferred() / Promise() ?

How do I utilize $.Deferred() / Promise() properly in this case?

这个问题在调试器中测试过,它会进入第一个函数(直到 $.get() 而实际上还没有得到任何东西)然后跳到第二个函数(再一次,向上$.get())。然后,它将继续执行第一个函数,直到它完成检索所有项目,然后当它进入第二个函数时,它会做同样的事情,但出于某种神秘的原因,videoIdChainStr 将所有视频 ID 保存在一个字符串中第一个函数从未被检索或执行,因为我怀疑它已经执行了第二个函数的 $.get(...) 并且当它具有值时再也没有执行过 "second time" 。为了解决这个问题,我想到了异步操作,一开始有点困惑,但在阅读了一些文章后,我理解了一些,但不是 100%,尤其是还没有在代码中,但我尝试了下面的代码:

我想真正了解如何在这种情况下使用 $.Deferred、resolve() 和 promise(),如果适用,还想了解其他方法,如 then(),因为我只需要解决并承诺 return 一个填充值 (videoIdChainStr) 到需要的 done() 并在之后执行第二个函数。

第一个函数:

var relatedVidsDefer = function relatedVids(videoId)
{
    var videoIdChainStr = null;
    var deferredVal = $.Deferred(); // instantiate defer object

    $.get( // get related videos related to videoId
        "https://www.googleapis.com/youtube/v3/search",
        {
            part: 'snippet',
            maxResults: vidResults,
            relatedToVideoId: videoId,
            order: 'relevance',
            type: 'video',
            key: 'XXXXXXX'
        },

        function(data)
        {
            $.each(data.items,
                function(i, item)
                {
                    try
                    {
                        console.log(item);
                        var vidTitle = item.snippet.title; // video title

                        var vidThumbUrl = item.snippet.thumbnails.default.url; 
                        var channelTitle = item.snippet.channelTitle;
                        var extractVideoId = null; // var to extract video id string from vidThumbUrl

                        // check if vidThumbUrl is not null, empty string, or undefined
                        if(vidThumbUrl)
                        {
                            var split = vidThumbUrl.split("/"); // split string when '/' seen
                            extractVideoId = split[4]; // retrieve the fourth index on the fourth '/'
                        }
                        else console.error("vidThumbUrl is either undefined or null or empty string.");

                        // if video title is longer than 25 characters, insert the three-dotted ellipse
                        if(vidTitle.length > 25)
                        {
                            var strNewVidTitle = vidTitle.substr(0, 25) + "...";
                            vidTitle = strNewVidTitle;
                        }





                        // check whether channelTitle is the same
                        if(channelTitle === "Channel Name")
                        {
                            extractedVideoIdArr.push(extractVideoId); // add the extracted video id to the array

                            // check if extractedVideoIdArr is not empty
                            if(extractedVideoIdArr !== 'undefined' && extractedVideoIdArr.length > 0)
                            {
                                videoIdChainStr = extractedVideoIdArr.join(", "); // change from an array to a chain string of videoIds for the relatedVidsDetails() 
                            }
                            deferredVal.resolve(videoIdChainStr); // get the value


                            var vidThumbnail = '<div class="video-thumbnail"><a class="thumb-link" href="single-video.html"><div class="video-overlay"><img src="imgs/video-play-button.png"/></div><img src="' + vidThumbUrl + '" alt="No Image Available." style="width:204px;height:128px"/></a><p><a class="thumb-link" href="single-video.html">' + vidTitle + '</a><br/></div>';

                            // print results
                            $('.thumb-related').append(vidThumbnail);
                            $(item).show(); // show current video thumbnail item 
                        }
                        else $(item).hide(); // hide current video thumbnail item
                    }
                    catch(err)
                    {
                        console.error(err.message); // log error but continue operation    
                    }
                }
            ); 
        }
    );
    return deferredVal.promise(); // return the value and execute the second function
};

第二个函数:

var relatedVidsDetailsDefer = function relatedVidsDetails(videoIdChainStr)
{
    // change extractvideoid into a string by tostring() or join() for param to recognize
    console.log("initial: ", extractedVideoIdArr);
    $.get(
        "https://www.googleapis.com/youtube/v3/videos",
        {
            part: 'snippet, contentDetails, statistics',
            id: videoIdChainStr, // chain string of video ids to be called upon in a single request
            key: 'XXXXXXX',
        },

        function(data)
        {
            $.each(data.items,
                function(i, item)
                {
                    try
                    {
                        var _vidDuration = item.contentDetails.duration;
                        var _viewCount = item.statistics.viewCount;
                        console.log("id: " + extractedVideoIdArr[i] + " duration: " + _vidDuration);
                        console.log("id: " + extractedVideoIdArr[i] + " viewCount: " + _viewCount);

                        $('.vidDetails').append(convert_time(_vidDuration) + ' / Views: ' + _viewCount);
                    }
                    catch(err)
                    {
                        console.error(err.message); // log error but continue operation    
                    }
                }
            );
        }
    );
};

执行:

relatedVidsDefer(_videoId).done(relatedVidsDetailsDefer); // wait till first function (before .done parameter) is complete before executing the second (in .done paramater)

更新:

代码更新为@valarauko 的回答。最后,它成功了,并且能够检索其中一个视频的详细信息,该视频与其余视频重复。进入第二个函数后,videoIdChainStr 只有第一个 id 而没有其余的……我假设 deferredVal.resolve(videoIdChainStr); 在第一个函数中第一次获得 id 时会立即解析循环,但不是字符串的其余部分,即使它继续循环并解析所有项目。

例如: 假设传ID1, ID2, ID3给参数,但只传了ID1。因此,为什么它只通过第二个函数的每个循环一次。我该如何解决这个问题?

之前执行第二个函数的原因是因为你在done语句中将括号附加到函数中(这会立即执行函数并将函数的结果赋值在done回调中。

relatedVidsDefer(_videoId).done(relatedVidsDetailsDefer());

应该是

relatedVidsDefer(_videoId).done(relatedVidsDetailsDefer);

此外,我不知道您是否有意使用全局变量 videoIdChainStr,但我建议您在本地创建它并提供 relatedVidsDetailsDefer 一个接收参数的函数(不应对第一个函数进行任何更改因为您已经使用该字符串解决了承诺。

第二个函数就像

var relatedVidsDetailsDefer = function relatedVidsDetails(videoIdChainStr) {...}

更新

如果您查看 jQuery 延迟文档 (link),resolved/rejected 承诺将忽略将来对 resolve/reject 和 return 数据的调用在第一个 resolved/rejected 上使用。另请注意,您只有一个 done 语句,因此无论如何您只检查第一个。

对于您的特定问题,您可以修改第二个函数以接收字符串数组并使用字符串数组解决承诺。