簡體   English   中英

JavaScript-同步等待異步操作(休眠)

[英]JavaScript - sync wait for async operation (sleep)

我知道它在這里被問過很多次,並且多次回答過,這不是應該怎么做的方法,但是再一次:)

是否可以通過某種方式調用異步功能(例如計時器/ ajax調用),基本上是常見的異步任務,並同步等待直到結束而沒有100%CPU使用率和瀏覽器阻塞?

簡單的答案就足夠了-是或否。 如果沒有,我必須以“異步方式”根據異步操作編寫所有代碼,否則會更好;)

想象一下:

 updateCSS("someurl.css") function updateCSS(url) { var css = getCachedResource(url); css = css.replace(/regexp/gm, function(curUrl) { base64 = atob(getCachedResource(curUrl)) return "data:image;base64," + base64 }) } function getCachedResource(url) { //... if (cached) { return cached } else { // doajax... // watfor return loaded } } 

當然,我可以使它異步,但是代碼會.....太糟糕了。 此外,我必須使調用此函數的所有函數異步。 您能看到簡單的解決方案嗎? 我會說阻塞是最簡單的。

謝謝

是。

您希望代碼“休眠”直到“異步功能(例如計時器/ ajax調用)...結束而沒有100%CPU使用率”。 這正是async / await所做的。

其他答案和評論都固定在您的短語“同步等待”上,在JS中這意味着阻塞,但是我了解您只是想避免不得不以“異步方式”重構代碼。 您想直接編寫,而不將所有結構構造為大量回調。

要使用async / await ,您首先必須接受promise-異步JavaScript的空間/時間。 然后,您需要使用類似Chrome或Firefox Developer Edition的現代瀏覽器,或使用Babel js轉換代碼。

接下來,您想看一下fetch ,它是一種返回承諾的現代方式來做ajax。

現在,您擁有了所需的一切。 這是在上述瀏覽器中可用的示例:

 async function fetchy(...args) { let response = await fetch(...args); if (!response.ok) throw new Error((await response.json()).error_message); return response; } async function showUser(user) { let url = `https://api.stackexchange.com/2.2/users/${user}?site=stackoverflow`; let item = (await (await fetchy(url)).json()).items[0]; console.log(JSON.stringify(item, null, 2)); let blob = await (await fetchy(item.profile_image)).blob(); img.src = URL.createObjectURL(blob); } input.oninput = async e => await showUser(e.target.value); showUser(input.value); 
 <input id="input" type="number" value="918910"></input><br> <img id="img"></img> <pre id="pre"></pre> 

僅供參考, fetchy是我的明智fetch錯誤處理的助手。

總結一下...

答案是不。 永遠不可能阻塞該函數或任何其他代碼,並等待異步操作完成。 JavaScript VM沒有直接支持它的機制。

尤其是在無法操縱JavaScript虛擬機事件循環的瀏覽器環境中(因為Node以及“去同步”庫實際上是如何做到的)是可能的。

這是因為這里提到的JavaScript並發模型和事件處理:

https://developer.mozilla.org/cs/docs/Web/JavaScript/EventLoop

它與JavaScript是單線程的事實無關。

另外,請注意,諸如Promises之類的所有內容和諸如await / async之類的新功能只是一種不同的方式,即如何為異步任務編寫/管理代碼。 它不是,也永遠不會在使異步同步方面有所幫助。

因此,不要像我一樣花時間嘗試找到一種方法來做。 而是通過設計異步代碼來花費它;)

,即使使用100%的CPU或瀏覽器被阻止,您也無法做到這一點。 事情經過精心設計,可以防止您這樣做。

這使得所有異步代碼都是有毒的 ,這意味着所有調用異步函數(基於promise或基於回調)的代碼本身都必須是異步的。

關於可怕的代碼: Promise鏈和async/await救援,它們可幫助您編寫語法更好的異步代碼。 不要害怕 ,尤其是如果您已經使用了Transpiler,請使用async/await

正如其他人指出的那樣,簡短的答案是否定的:在使用Javascript的大多數環境中,Javascript是單線程的。 您將要發生的事情是在getCachedResource()的另一個回調中處理CSS

像這樣:

function updateCSS(url) {
    getCachedResource(url, function(css) {
        // process the css
    });
}

function getCachedResource(url, done) {
    //...
    if (cached) {
        cachedCSS = ...
        done(cachedCSS);
    } else {
        $.ajax(
        {
            // ...
            success: function(result) {
                // Get the css from the response
                done(css);
            }
            // ...
        });
    }
}

查看jQuery“延遲”對象或Javascript許諾,以更完整地處理回調

簡而言之,答案是否定的。

不要這樣想。 考慮后端性能,而不是在客戶端。

是否可以通過某種方式調用異步功能(例如計時器/ ajax調用),基本上是常見的異步任務,並同步等待直到結束而沒有100%CPU使用率和瀏覽器阻塞?

不可以。您無法“同步等待” Javascript中的異步結果。 由於它是單線程和事件驅動的設計,因此無法正常工作。

由於有關該問題的大多數其他談話/評論本質上都是概念性的,因此我想就如何使用適當的異步設計實際解決您的特定問題提供答案。

當一些耗時的活動完成時,JavaScript不提供睡眠功能。 因此,您不能將updateCSS()函數設計為具有純同步接口。 相反,您將需要使用Javascript中的異步工具來設計一個接口,該接口既適用於緩存資源的情況,也適用於必須通過網絡獲取資源的情況。

通常的設計方法是設計一個異步接口,如果碰巧緩存了資源,您仍然可以通過異步接口為其提供服務。 然后,您有一個調用者可以使用的接口,並且無論是否緩存資源,該接口始終有效。 它要求調用者使用異步接口。

實際上,您可以像這樣設計getCachedResource()

function getCachedResource(url) {
  //...
  if (cached) {
    // returns a promise who's resolved value is the resource
    return Promise.resolve(cached);
  } else {
    // returns a promise who's resolved value is the resource
    return doAjax(...);
  }
}

無論是否緩存該值, getCachedResource()始終返回一個promise。 因此,調用方始終使用promise接口,如下所示:

getCachedResource(...).then(function(result) {
    // use the result here
});

如果結果恰好被緩存,則.then()處理函數將立即(在下一個刻度上)被調用。 如果通過網絡檢索結果,則將在結果可用時立即調用.then()處理函數。 調用者不必擔心在何處或如何檢索資源-他們有一個接口來獲取它。

updateCSS()函數中,您將必須設計使用getCachedResource()的異步接口。 因為您當前的設計在css.replace()使用了同步回調, css.replace()您必須重新考慮一下以適應異步檢索。 這是一種實現方法。

function updateCSS(url) {
   return getCachedResource(url).then(function(css) {
       // preflight the replacements we need to do so we know what to fetch
       var urls = [];
       css.replace(/regexp/gm, function(curUrl) {
           urls.push(curUrl);
       });
       // now load all the resources
       return Promise.all(urls.map(getCachedResource)).then(function(resources) {
           // now have all the resources, in proper order
           var index = 0;
           css = css.replace(/regexp/gm, function(curUrl) {
               return resources[index++];
           });
           // final resolved value is the fully loaded css
           return css;
       })
   });
}

這里的方案是運行一個虛擬的.replace() ,它不會保留結果以生成您需要加載的URL列表。 根據您的正則表達式,也可以使用.match().exec()完成此操作。 然后,一旦您有要獲取的URL列表,就將它們全部獲取。 擁有所有這些資源后,就可以真正運行.replace() ,因為您有可用於同步.replace()回調的資源。

然后可以這樣使用:

updateCss(someCSSURL).then(function(css) {
    // use the CSS here
}).catch(function(err) {
    // process error here
});

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM