以前我在單執行緒情況下問過這個問題,并得到了一個在這種情況下有效的答案。但現在我試圖在多執行緒環境中回答同樣的問題。
我正在使用 OpenLayers 繪制英國軍械測量地圖。我有作業示例,但他們通過在 URL 中提供密鑰來使用不太安全的方法來訪問 UKOS 服務器,而我想使用更安全的 OAuth2 安排。為此,我已經有一個 CGI 腳本查詢服務器以獲取會話令牌,并將其作為 JSON 回傳。在單執行緒環境下可以通過讓地圖繪制頁面等待其回傳來解決該問題,如下:
UKOS.getToken = async function () {
if (
!UKOS.token ||
new Date().getTime() >
UKOS.token.issued_at 1000 * (UKOS.token.expires_in - 15)
)
UKOS.token = await fetch(/* CGI Script */).then((response) =>
response.json()
);
console.log(UKOS.token.access_token);
return UKOS.token.access_token;
};
// ...
UKOS.getToken()
.then(token => fetch(UKOS.WMTS "&request=GetCapabilities&version=2.0.0", { headers: {Authorization: "Bearer " token} }) )
.then( /* etc */ )
但是,現在我使用的是多執行緒環境,以上不再有效,因為在第一次呼叫 getToken() 啟動獲取令牌后,后續呼叫在回傳之前崩潰,并且取決于正是我嘗試撰寫 getToken() 的方式,要么在一個可以執行的時候啟動多個令牌獲取(直到它過期),要么不等待回傳令牌,并且因為它仍然為空而失敗。這是我目前嘗試改編的 getToken() ...
UKOS = {
token: null,
tokStat: 0,
tokProm: null
}
UKOS.getToken = async function() {
var result;
console.log( "UKOS.tokStat: " UKOS.tokStat );
switch( UKOS.tokStat ) {
case 1: await UKOS.tokProm;
await UKOS.token;
// Deliberate fall through
console.log( "UKOS.token: " UKOS.token );
console.log( "Falling through from 1 to 2 ..." );
case 2: if( new Date().getTime() < UKOS.token.issued_at 1000*(UKOS.token.expires_in - 15) ) {
result = UKOS.token.access_token;
break;
}
// Deliberate fall through
console.log( "Falling through from 2 to 0 ..." );
case 0: UKOS.tokStat = 1;
UKOS.tokProm = fetch( location.protocol '//' location.host "/cgi-bin/OSDataOAuth.py" );
UKOS.token = await UKOS.tokProm.then( response => response.json() );
UKOS.tokStat = 2;
result = UKOS.token.access_token;
}
return result;
};
如果沒有兩個 'await' 限定符,隨后的呼叫當然會崩潰到 'case 2' 并失敗,因為令牌仍然為空,有了它們,這就是結果 [不相關的細節被洗掉] ...
// Debugging console log message from routine calling getToken()
url: https://api.os.uk/maps/raster/v1/wmts?service=WMTS&REQUEST=G…TRIX=EPSG:27700:0&TILEROW=6&TILECOL=1&FORMAT=image/png
// Debugging console log message from getToken()
UKOS.tokStat: 0
url: https://api.os.uk/maps/raster/v1/wmts?service=WMTS&REQUEST=G…TRIX=EPSG:27700:0&TILEROW=5&TILECOL=1&FORMAT=image/png
UKOS.tokStat: 1
[Repeated multiple times]
url: undefined
url: https://api.os.uk/maps/raster/v1/wmts?service=WMTS&REQUEST=G…TRIX=EPSG:27700:0&TILEROW=6&TILECOL=1&FORMAT=image/png
[Repeated multiple times]
url: https://api.os.uk/maps/raster/v1/wmts?service=WMTS&REQUEST=G…TRIX=EPSG:27700:0&TILEROW=6&TILECOL=2&FORMAT=image/png
UKOS.tokStat: 1
// Token fetched successfully
// (see end, the only map tile fetch that succeeds is almost certainly the first to call getToken())
XHRGET http://local.localhost/cgi-bin/OSDataOAuth.py
[HTTP/1.1 200 OK 1590ms]
url: https://api.os.uk/maps/raster/v1/wmts?service=WMTS&REQUEST=G…TRIX=EPSG:27700:0&TILEROW=5&TILECOL=2&FORMAT=image/png
UKOS.tokStat: 1
[Repeated multiple times]
UKOS.token: null
Falling through from 1 to 2 ...
[Repeated multiple times]
Falling through from 1 to 2 ...
Uncaught (in promise) TypeError: UKOS.token is null
getToken http://local.localhost/JavaJive/ProgScriptWeb/UKOSOpenLayers2.shtml:206
setImgSrc http://local.localhost/JavaJive/ProgScriptWeb/UKOSOpenLayers2.shtml:301
initImage https://openlayers.org/api/2.13/OpenLayers.debug.js:29733
renderTile https://openlayers.org/api/2.13/OpenLayers.debug.js:29608
draw https://openlayers.org/api/2.13/OpenLayers.debug.js:29581
initGriddedTiles https://openlayers.org/api/2.13/OpenLayers.debug.js:30951
moveTo https://openlayers.org/api/2.13/OpenLayers.debug.js:30422
moveTo https://openlayers.org/api/2.13/OpenLayers.debug.js:83261
moveTo https://openlayers.org/api/2.13/OpenLayers.debug.js:9273
setCenter https://openlayers.org/api/2.13/OpenLayers.debug.js:9035
setBaseLayer http://local.localhost/JavaJive/ProgScriptWeb/UKOSOpenLayers2.shtml:442
addLayer https://openlayers.org/api/2.13/OpenLayers.debug.js:8354
initOSMap http://local.localhost/JavaJive/ProgScriptWeb/UKOSOpenLayers2.shtml:457
onl oad http://local.localhost/JavaJive/ProgScriptWeb/UKOSOpenLayers2.shtml:1
UKOSOpenLayers2.shtml:206:34
[Repeated multiple times]
// I think this one, the only one to successfully fetch a map tile is probably the first attempt to do so, which correctly waited for the returned token
XHRGET https://api.os.uk/maps/raster/v1/wmts?service=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=Leisure_27700&STYLE=default&TILEMATRIXSET=EPSG:27700&TILEMATRIX=EPSG:27700:0&TILEROW=6&TILECOL=1&FORMAT=image/png
[HTTP/1.1 200 OK 1417ms]
dataUrl: blob:http://local.localhost/2f7fa455-3385-4d10-ab0e-b70999658137
What is interesting about the above is that the two 'await's do appear to be accomplishing something, but not enough, because without them each ...
UKOS.tokStat: 1
... is immediately followed by ...
UKOS.token: null
Falling through from 1 to 2 ...
... yet they're obviously not waiting for the token to be returned, as evidenced by the fact that they all fail before the first call ultimately succeeds. Can anyone suggest how to make the second and subsequent calls wait for the token requested by the first call?
uj5u.com熱心網友回復:
在您的原始代碼中,該函式有兩條路徑。
- 你已經有一個令牌
- 把它返還
- 您還沒有令牌
- 發出異步請求以獲取一個
- 當請求解決時存盤令牌
- 把它返還
當您連續兩次快速呼叫該函式時,您會跌入 2.1 和 2.2 之間的差距。
停止存盤令牌。改為存盤2.1 回傳的承諾。
這樣,如果請求正在進行中,您可以重用它。
UKOS.getToken = async function () {
if (UKOS.tokenPromise) {
const token = await UKOS.tokenPromise;
const now = new Date().getTime();
if (now <= token.issued_at 1000 * (token.expires_in - 15)) {
// Token still good
return token.access_token;
}
// If we get here, the token has expired
}
// If we get here, the token has expired or we didn't have a promise in the first place
// Create or update token promise
UKOS.tokenPromise = fetch(/* CGI Script */).then((response) => response.json());
// Wait for it to resolve
const token = UKOS.tokenPromise;
// Assume the new token is good
return UKOS.token.access_token;
};
uj5u.com熱心網友回復:
這是我的原始代碼的作業版本,其中包含 Quentin 方法所建議的更改,盡管它比我最初想象的要作業一點點,但我仍然非常感激,因為它教會了我如何解決這個問題:
UKOS.getToken = async function() {
var result;
console.log( "UKOS.tokStat: " UKOS.tokStat );
do
switch( UKOS.tokStat ) {
case 1: UKOS.token = await UKOS.tokProm;
UKOS.tokStat = 2;
// Deliberate fall through
console.log( "UKOS.token: " UKOS.token );
console.log( "Falling through from 1 to 2 ..." );
case 2: if( new Date().getTime() < UKOS.token.issued_at 1000*(UKOS.token.expires_in - 15) )
{
result = UKOS.token.access_token;
break;
}
// Deliberate fall through
console.log( "Falling through from 2 to 0 ..." );
case 0: UKOS.tokProm = null;
UKOS.token = null;
UKOS.tokStat = 1;
// First call arrives here, but without 'await' in the
// the statement below it fails by returning null while
// later calls work. However, if we put 'await' here,
// then the first call succeeds while later calls find
// UKOS.tokProm = null and so fail. Therefore somehow
// we must process first and later calls the same, and
// this happens by the 'while' statement, which forces
// the first call back through the 'switch' statement.
UKOS.tokProm = fetch( location.protocol '//' location.host "/cgi-bin/OSDataOAuth.py" )
.then( response => response.json() );
}
while( !UKOS.token )
return result;
};
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/424583.html
標籤:javascript multithreading asynchronous
