在调试器中测试的问题是,它将进入第一个函数(直到 $.get()
尚未实际获取任何内容),然后跳到第二个函数(再次,直到 $.get()
)。然后,它将继续执行第一个函数,直到完成检索所有项目,然后当它进入第二个函数时,它将执行相同的操作,但由于某种神秘的原因,videoIdChainStr 将所有视频 ID 保存在字符串中第一个函数从未被检索或执行,因为我怀疑它已经执行了第二个函数的 $.get(...)
,并且当它具有值时再也没有“第二次”执行它。为了解决这个问题,我想到了异步操作,一开始有点令人困惑,但在阅读了一些文章之后,我理解了一些,但不是 100%,尤其是还没有在代码中,但我尝试了下面的代码:
我想真正了解如何在这种情况下使用 $.Deferred、resolve() 和 Promise(),如果适用的话,其他方法如 then(),因为我只需要一个解析并 promise 返回一个填充的value (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 一个接收参数的函数(不应对第一个函数进行任何更改,因为您已经用该字符串解决了 promise 。
第二个函数就像
var relatedVidsDetailsDefer = function relatedVidsDetails(videoIdChainStr) {...}
更新
如果您查看 jQuery 延迟文档 ( link ),已解决/拒绝的 Promise 将忽略 future 对解决/拒绝的调用,并返回第一个已解决/拒绝的数据。另请注意,您只有一个完成语句,因此您只检查第一个语句。
对于您的特定问题,您可以修改第二个函数以接收字符串数组并使用字符串数组解决 promise 。
关于javascript - 在这种情况下如何正确利用 $.Deferred()/Promise() ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36451559/