Node.js中怎么實(shí)現(xiàn)線程睡眠,針對(duì)這個(gè)問(wèn)題,這篇文章詳細(xì)介紹了相對(duì)應(yīng)的分析和解答,希望可以幫助更多想解決這個(gè)問(wèn)題的小伙伴找到更簡(jiǎn)單易行的方法。
新晃ssl適用于網(wǎng)站、小程序/APP、API接口等需要進(jìn)行數(shù)據(jù)傳輸應(yīng)用場(chǎng)景,ssl證書(shū)未來(lái)市場(chǎng)廣闊!成為創(chuàng)新互聯(lián)建站的ssl證書(shū)銷(xiāo)售渠道,可以享受市場(chǎng)價(jià)格4-6折優(yōu)惠!如果有意向歡迎電話聯(lián)系或者加微信:13518219792(備注:SSL證書(shū)合作)期待與您的合作!
一:糟糕的 “循環(huán)空轉(zhuǎn)”
下面這段代碼是糟糕的,Node.js 是以單進(jìn)程、單線程的方式啟動(dòng),所有的業(yè)務(wù)代碼都工作在主線程,這樣會(huì)造成 CPU 持續(xù)占用,主線程阻塞對(duì) CPU 資源也是一種浪費(fèi),與真正的線程睡眠相差甚遠(yuǎn)。
const start = new Date(); while (new Date() - start < 2000) {}
運(yùn)行之后如上圖所示,CPU 暴漲,同時(shí)也會(huì)破壞事件循環(huán)調(diào)度,導(dǎo)致其它任務(wù)無(wú)法執(zhí)行。
二:定時(shí)器 + Promise 實(shí)現(xiàn) sleep
通過(guò)定時(shí)器延遲執(zhí)行函數(shù) setTimeout + Promise 的鏈?zhǔn)揭蕾噷?shí)現(xiàn),本質(zhì)是創(chuàng)建一個(gè)新的 Promise 對(duì)象,待定時(shí)器延遲時(shí)間到了執(zhí)行 resolve 函數(shù)這時(shí) then 才會(huì)執(zhí)行,這里 Node.js 執(zhí)行線程是沒(méi)有進(jìn)行睡眠的,事件循環(huán)和 V8 等都是正常運(yùn)行的。但這也是目前通用的一種解決方案,因?yàn)槟悴荒茏屩骶€程阻塞,否則程序就無(wú)法繼續(xù)工作了。
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
在 Node.js 中還可以利用 util 模塊提供的 promisify 方法實(shí)現(xiàn),一種快捷方式。
const { promisify } = require('util'); const sleep = promisify(setTimeout);
因?yàn)槭腔诙〞r(shí)器與 Promise 所以也自然是異步的方式了,使用時(shí)也要注意,如下所示:
// async await 的方式 async function test() { console.log(1); await sleep(3000); console.log(2); } // Promise 的鏈?zhǔn)秸{(diào)用方式 async function test() { console.log(1); sleep(3000).then(() => { console.log(2); }); }
三:零 CPU 開(kāi)銷(xiāo)真正的事件循環(huán)阻止 sleep 實(shí)現(xiàn)
ECMA262 草案提供了 Atomics.wait API 來(lái)實(shí)現(xiàn)線程睡眠,它會(huì)真正的阻塞事件循環(huán),阻塞線程直到超時(shí)。
該方法 Atomics.wait(Int32Array, index, value[, timeout]) 會(huì)驗(yàn)證給定的 Int32Array 數(shù)組位置中是否仍包含其值,在休眠狀態(tài)下會(huì)等待喚醒或直到超時(shí),返回一個(gè)字符串表示超時(shí)還是被喚醒。
同樣的因?yàn)槲覀兊臉I(yè)務(wù)是工作在主線程,避免在主線程中使用,在 Node.js 的工作線程中可以根據(jù)實(shí)際需要使用。
/** * 真正的阻塞事件循環(huán),阻塞線程直到超時(shí),不要在主線程上使用 * @param {Number} ms delay * @returns {String} ok|not-equal|timed-out */ function sleep(ms) { const valid = ms > 0 && ms < Infinity; if (valid === false) { if (typeof ms !== 'number' && typeof ms !== 'bigint') { throw TypeError('ms must be a number'); } throw RangeError('ms must be a number that is greater than 0 but less than Infinity'); } return Atomics.wait(int32, 0, 0, Number(ms)) } sleep(3000)
由于本節(jié)我們僅是在講解 sleep 的實(shí)現(xiàn),所以關(guān)于 Atomics.wait 方法睡眠之后如何被其它線程喚醒也不再此處講了,之后我會(huì)寫(xiě)一講 Node.js 中的工作線程相關(guān)文章,到時(shí)會(huì)再次介紹。
四:基于 N-API 擴(kuò)展使用 C 語(yǔ)言實(shí)現(xiàn) sleep
通過(guò) Addon 的方式使用 N-API 編寫(xiě) C/C++ 插件,借助其提供的系統(tǒng) sleep() 函數(shù)實(shí)現(xiàn)。
// sleep.c #include#include #include napi_value sleepFn(napi_env env, napi_callback_info info) { napi_status status; size_t argc = 1; napi_value argv[1]; status = napi_get_cb_info(env, info, &argc, argv, NULL, NULL); assert(status == napi_ok); if (argc < 1) { napi_throw_type_error(env, NULL, "ms is required"); return NULL; } napi_valuetype valueType; napi_typeof(env, argv[0], &valueType); if (valueType != napi_number) { napi_throw_type_error(env, NULL, "ms must be a number"); return NULL; } int64_t s; napi_get_value_int64(env, argv[0], &s); sleep(s); return NULL; } napi_value init(napi_env env, napi_value exports) { napi_status status; napi_property_descriptor descriptor = { "sleep", 0, sleepFn, 0, 0, 0, napi_default, 0 }; status = napi_define_properties(env, exports, 1, &descriptor); assert(status == napi_ok); return exports; } NAPI_MODULE(sleep, init);
經(jīng)過(guò)一系列編譯之后,引入 .node 文件直接使用。
// app.js const { sleep } = require('./build/Release/sleep.node'); sleep(3);
五:easy-sleep 模塊
這是筆者寫(xiě)的一個(gè)小模塊 https://github.com/qufei1993/easy-sleep,其實(shí)也是對(duì)以上幾種方法的整合,包含了 C 插件的編寫(xiě),使用如下:
// Install npm install easy-sleep -S // Async sleep const { sleep } = require('easy-sleep'); await sleep(3000); // Thread sleep const { Thread } = require('easy-sleep'); Thread.sleep();
關(guān)于Node.js中怎么實(shí)現(xiàn)線程睡眠問(wèn)題的解答就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,如果你還有很多疑惑沒(méi)有解開(kāi),可以關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道了解更多相關(guān)知識(shí)。