同步代码就像在快餐店点单:你必须站在柜台前等待餐点制作完成才能离开,期间什么都做不了。而异步代码则像高级餐厅的点餐服务——下单后服务员会给你一个呼叫器,你可以自由活动,等餐点准备好时呼叫器会通知你。
在JavaScript引擎中,这种差异体现在执行机制上:
javascript复制// 同步示例
console.log('开始做饭'); // 立即执行
cookRice(); // 必须等待饭做好
console.log('做饭结束'); // 必须等cookRice()完成后执行
// 异步示例
console.log('开始点外卖');
setTimeout(() => {
console.log('外卖送达');
}, 3000);
console.log('可以继续工作'); // 不需要等待setTimeout
JavaScript的异步能力依赖于事件循环(Event Loop)系统,其工作流程可分为四个关键部分:
javascript复制console.log('Script开始'); // 1
setTimeout(() => {
console.log('setTimeout回调'); // 4
}, 0);
Promise.resolve().then(() => {
console.log('Promise微任务'); // 3
});
console.log('Script结束'); // 2
注意:微任务(Promise、MutationObserver)优先级高于宏任务(setTimeout、setInterval)
早期的JavaScript异步处理完全依赖回调函数,形成了著名的"回调地狱"问题:
javascript复制getUser(userId, (user) => {
getPermissions(user.id, (permissions) => {
getMenu(permissions.level, (menu) => {
renderMenu(menu, () => {
logAction('menu_rendered', () => {
// 更多嵌套...
});
});
});
});
});
典型问题:
ES6引入的Promise提供了更结构化的异步处理方式:
javascript复制function getUser(userId) {
return new Promise((resolve, reject) => {
// 模拟异步操作
setTimeout(() => {
if (userId) {
resolve({ id: 123, name: '张三' });
} else {
reject(new Error('用户ID不能为空'));
}
}, 500);
});
}
getUser('123')
.then(user => getPermissions(user.id))
.then(perms => getMenu(perms.level))
.then(menu => renderMenu(menu))
.catch(err => console.error('流程出错:', err));
Promise核心特点:
ES2017引入的async/await让异步代码拥有同步代码的可读性:
javascript复制async function loadUserMenu() {
try {
const user = await getUser(userId);
const perms = await getPermissions(user.id);
const menu = await getMenu(perms.level);
return renderMenu(menu);
} catch (error) {
console.error('加载失败:', error);
throw error; // 可以选择继续抛出
}
}
关键规则:
async函数总是返回Promiseawait只能在async函数中使用await会暂停函数执行,直到Promise解决并行请求优化:
javascript复制// 串行方式(总时长=各请求时长之和)
async function serialFetch() {
const user = await fetch('/user');
const posts = await fetch('/posts');
const comments = await fetch('/comments');
return { user, posts, comments };
}
// 并行方式(总时长≈最慢的请求)
async function parallelFetch() {
const [user, posts, comments] = await Promise.all([
fetch('/user'),
fetch('/posts'),
await fetch('/comments')
]);
return { user, posts, comments };
}
分页加载示例:
javascript复制async function loadPaginatedData(pageSize = 10) {
const allData = [];
let currentPage = 1;
while (true) {
const pageData = await fetch(`/api/data?page=${currentPage}&size=${pageSize}`);
if (pageData.length === 0) break;
allData.push(...pageData);
currentPage++;
// 添加延迟避免阻塞UI
if (currentPage % 3 === 0) {
await new Promise(r => setTimeout(r, 100));
}
}
return allData;
}
错误重试机制:
javascript复制async function fetchWithRetry(url, options = {}, maxRetries = 3) {
let lastError;
for (let i = 0; i < maxRetries; i++) {
try {
const response = await fetch(url, options);
if (!response.ok) throw new Error(`HTTP ${response.status}`);
return await response.json();
} catch (error) {
lastError = error;
if (i < maxRetries - 1) {
const delay = Math.pow(2, i) * 1000; // 指数退避
await new Promise(r => setTimeout(r, delay));
}
}
}
throw lastError;
}
错误边界分类处理:
javascript复制async function handlePayment() {
try {
const result = await processPayment();
return result;
} catch (error) {
if (error instanceof NetworkError) {
// 网络问题特殊处理
await showNetworkErrorToast();
throw new UserFriendlyError('请检查网络连接');
} else if (error instanceof PaymentError) {
// 支付失败处理
logPaymentFailure(error);
throw new UserFriendlyError('支付处理失败');
} else {
// 未知错误
logUnexpectedError(error);
throw new UserFriendlyError('系统繁忙,请稍后再试');
}
}
}
请求取消与竞态处理:
javascript复制let abortController = null;
async function searchProducts(keyword) {
// 取消之前的请求
abortController?.abort();
// 创建新的AbortController
abortController = new AbortController();
try {
const response = await fetch(`/api/search?q=${keyword}`, {
signal: abortController.signal
});
const data = await response.json();
return data;
} catch (error) {
if (error.name !== 'AbortError') {
console.error('搜索失败:', error);
throw error;
}
}
}
请求优先级管理:
javascript复制const highPriorityQueue = [];
const lowPriorityQueue = [];
async function processQueue() {
while (highPriorityQueue.length > 0) {
const task = highPriorityQueue.shift();
await task();
}
if (lowPriorityQueue.length > 0) {
// 空闲时处理低优先级任务
requestIdleCallback(async () => {
const task = lowPriorityQueue.shift();
await task();
});
}
}
// 添加任务示例
function addSearchTask(keyword, isUrgent = false) {
const task = async () => {
const results = await searchProducts(keyword);
updateUI(results);
};
if (isUrgent) {
highPriorityQueue.push(task);
} else {
lowPriorityQueue.push(task);
}
processQueue();
}
Chrome DevTools 关键功能:
Performance面板:
Network面板:
Memory面板:
实用调试代码片段:
javascript复制// 给Promise添加调试标签
Promise.prototype.withTag = function(tag) {
this._tag = tag;
return this;
};
// 示例使用
fetch('/api/data')
.withTag('user-profile')
.then(data => console.log(data));
// 在控制台查看未完成的Promise
function listPendingPromises() {
return Array.from(document.querySelectorAll('iframe'))
.map(iframe => {
try {
return Array.from(iframe.contentWindow.Promise._asyncStack || []);
} catch (e) {
return [];
}
})
.flat()
.filter(stack => stack && stack.state === 'pending');
}
自定义Hook封装:
javascript复制import { useState, useEffect, useRef } from 'react';
export function useAsync(asyncFn, immediate = true) {
const [status, setStatus] = useState('idle');
const [data, setData] = useState(null);
const [error, setError] = useState(null);
const mountedRef = useRef(true);
const execute = async (...args) => {
setStatus('pending');
setError(null);
try {
const result = await asyncFn(...args);
if (mountedRef.current) {
setData(result);
setStatus('success');
}
return result;
} catch (error) {
if (mountedRef.current) {
setError(error);
setStatus('error');
}
throw error;
}
};
useEffect(() => {
if (immediate) {
execute();
}
return () => {
mountedRef.current = false;
};
}, []);
return { execute, status, data, error };
}
Suspense与错误边界组合:
javascript复制// 异步资源封装
function createResource(promise) {
let status = 'pending';
let result;
const suspender = promise.then(
value => {
status = 'success';
result = value;
},
error => {
status = 'error';
result = error;
}
);
return {
read() {
if (status === 'pending') throw suspender;
if (status === 'error') throw result;
return result;
}
};
}
// 组件中使用
function UserProfile({ userId }) {
const resource = useMemo(() => {
return createResource(fetchUser(userId));
}, [userId]);
const user = resource.read();
return (
<div>
<h1>{user.name}</h1>
{/* 渲染用户详情 */}
</div>
);
}
// 外层包裹
function App() {
return (
<ErrorBoundary fallback={<ErrorPage />}>
<Suspense fallback={<Spinner />}>
<UserProfile userId="123" />
</Suspense>
</ErrorBoundary>
);
}
组合函数封装:
javascript复制import { ref, onUnmounted } from 'vue';
export function useAsync(asyncFn, options = {}) {
const { immediate = true } = options;
const data = ref(null);
const error = ref(null);
const status = ref('idle');
let abortController = null;
const execute = async (...args) => {
abortController?.abort();
abortController = new AbortController();
status.value = 'pending';
error.value = null;
try {
const result = await asyncFn(...args, { signal: abortController.signal });
data.value = result;
status.value = 'success';
return result;
} catch (err) {
if (err.name !== 'AbortError') {
error.value = err;
status.value = 'error';
}
throw err;
}
};
if (immediate) {
execute();
}
onUnmounted(() => {
abortController?.abort();
});
return {
data,
error,
status,
execute,
isLoading: computed(() => status.value === 'pending')
};
}
组件中使用示例:
vue复制<script setup>
import { useAsync } from './useAsync';
const {
data: posts,
error,
execute: reload,
isLoading
} = useAsync(() =>
fetch('/api/posts').then(r => r.json())
);
</script>
<template>
<div>
<button @click="reload" :disabled="isLoading">
{{ isLoading ? '加载中...' : '刷新数据' }}
</button>
<div v-if="error" class="error">
加载失败: {{ error.message }}
</div>
<ul v-else-if="posts">
<li v-for="post in posts" :key="post.id">
{{ post.title }}
</li>
</ul>
</div>
</template>
javascript复制const fs = require('fs');
const zlib = require('zlib');
const { pipeline } = require('stream/promises');
async function processLargeFile(inputPath, outputPath) {
try {
await pipeline(
fs.createReadStream(inputPath),
zlib.createGzip(),
fs.createWriteStream(outputPath)
);
console.log('文件处理完成');
} catch (error) {
console.error('处理失败:', error);
}
}
连接池管理:
javascript复制const { Pool } = require('pg');
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
max: 20, // 最大连接数
idleTimeoutMillis: 30000,
connectionTimeoutMillis: 2000,
});
async function queryWithRetry(sql, params, retries = 3) {
let lastError;
for (let i = 0; i < retries; i++) {
const client = await pool.connect();
try {
const result = await client.query(sql, params);
client.release();
return result;
} catch (error) {
client.release();
lastError = error;
if (i < retries - 1) {
await new Promise(r => setTimeout(r, 100 * Math.pow(2, i)));
}
}
}
throw lastError;
}
javascript复制const { CronJob } = require('cron');
const { Worker } = require('worker_threads');
function startScheduledTask() {
// 每天凌晨3点执行
new CronJob(
'0 3 * * *',
() => {
const worker = new Worker('./data-processing-worker.js');
worker.on('message', (msg) => {
console.log('Worker message:', msg);
});
worker.on('error', (err) => {
console.error('Worker error:', err);
});
worker.on('exit', (code) => {
if (code !== 0) {
console.error(`Worker stopped with exit code ${code}`);
}
});
},
null,
true,
'Asia/Shanghai'
);
}
javascript复制class AsyncEventEmitter {
constructor() {
this.listeners = new Map();
}
on(event, listener) {
if (!this.listeners.has(event)) {
this.listeners.set(event, new Set());
}
this.listeners.get(event).add(listener);
}
off(event, listener) {
if (this.listeners.has(event)) {
this.listeners.get(event).delete(listener);
}
}
async emit(event, ...args) {
if (this.listeners.has(event)) {
const promises = [];
for (const listener of this.listeners.get(event)) {
promises.push(listener(...args));
}
return Promise.all(promises);
}
return Promise.resolve();
}
}
// 使用示例
const emitter = new AsyncEventEmitter();
emitter.on('data', async (data) => {
await processData(data);
});
emitter.on('data', async (data) => {
await logData(data);
});
async function receiveData(data) {
await emitter.emit('data', data);
console.log('所有数据处理完成');
}
javascript复制class AsyncQueue {
constructor(concurrency = 1) {
this.tasks = [];
this.active = 0;
this.concurrency = concurrency;
this.drainListeners = [];
}
add(task) {
this.tasks.push(task);
this.next();
}
next() {
if (this.active >= this.concurrency || this.tasks.length === 0) {
if (this.active === 0 && this.tasks.length === 0) {
this.notifyDrain();
}
return;
}
this.active++;
const task = this.tasks.shift();
Promise.resolve(task())
.catch(error => {
console.error('任务执行失败:', error);
})
.finally(() => {
this.active--;
this.next();
});
}
onDrain(listener) {
this.drainListeners.push(listener);
}
notifyDrain() {
for (const listener of this.drainListeners) {
listener();
}
}
}
// 使用示例
const queue = new AsyncQueue(3);
for (let i = 0; i < 10; i++) {
queue.add(async () => {
console.log(`开始任务 ${i}`);
await new Promise(r => setTimeout(r, 1000));
console.log(`完成任务 ${i}`);
});
}
queue.onDrain(() => {
console.log('所有任务完成');
});
Jest测试示例:
javascript复制describe('异步函数测试', () => {
// 普通Promise测试
test('fetchData返回正确数据', () => {
return fetchData().then(data => {
expect(data).toEqual({ id: 1, name: '测试数据' });
});
});
// async/await测试
test('asyncFetchData返回正确数据', async () => {
const data = await asyncFetchData();
expect(data.id).toBe(1);
});
// 错误测试
test('fetchWithError抛出异常', async () => {
await expect(fetchWithError()).rejects.toThrow('请求失败');
});
// 定时器测试
jest.useFakeTimers();
test('延迟函数正确执行', async () => {
const callback = jest.fn();
delayedFunction(callback);
jest.runAllTimers();
await Promise.resolve(); // 确保所有微任务执行完毕
expect(callback).toHaveBeenCalledWith('完成');
});
});
VSCode调试配置:
json复制{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "调试异步测试",
"skipFiles": ["<node_internals>/**"],
"program": "${workspaceFolder}/node_modules/jest/bin/jest",
"args": ["--runInBand", "--verbose"],
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen",
"timeout": 10000
},
{
"type": "chrome",
"request": "launch",
"name": "调试前端异步",
"url": "http://localhost:3000",
"webRoot": "${workspaceFolder}/src",
"breakOnLoad": true,
"sourceMapPathOverrides": {
"webpack:///./src/*": "${webRoot}/*"
}
}
]
}
实用调试代码:
javascript复制// 异步堆栈追踪增强
Error.stackTraceLimit = 50;
// 给Promise添加异步上下文
global.Promise = class TrackedPromise extends Promise {
constructor(executor) {
const stack = new Error().stack.split('\n').slice(2).join('\n');
super((resolve, reject) => {
executor(
value => {
console.log(`Promise resolved: ${stack}`);
resolve(value);
},
error => {
console.error(`Promise rejected: ${stack}`);
reject(error);
}
);
});
}
};
// 长异步操作标记
async function trackLongOperation(name, promise, timeout = 1000) {
let timer = setTimeout(() => {
console.warn(`长时间运行的操作: ${name}`);
}, timeout);
try {
return await promise;
} finally {
clearTimeout(timer);
}
}
javascript复制// 安全封装示例
async function withCleanup(resourceFn, callback) {
let resource;
try {
resource = await resourceFn();
return await callback(resource);
} finally {
if (resource && typeof resource.cleanup === 'function') {
await resource.cleanup();
}
}
}
// 使用示例
await withCleanup(
() => createDatabaseConnection(),
async (db) => {
const result = await db.query('SELECT * FROM users');
processResults(result);
}
);
javascript复制function createRaceGuard() {
let lastId = 0;
return async function guard(fn) {
const currentId = ++lastId;
const result = await fn();
if (currentId !== lastId) {
throw new Error('操作被新请求覆盖');
}
return result;
};
}
// 使用示例
const guardedFetch = createRaceGuard();
async function search(query) {
return guardedFetch(async () => {
const results = await fetchResults(query);
return processResults(results);
});
}
javascript复制async function parallelWithLimit(tasks, limit) {
const results = [];
const executing = new Set();
for (const task of tasks) {
if (executing.size >= limit) {
await Promise.race(executing);
}
const promise = task().then(result => {
executing.delete(promise);
return result;
});
executing.add(promise);
results.push(promise);
}
return Promise.all(results);
}
// 使用示例
const urls = ['url1', 'url2', 'url3', ...];
const fetchTasks = urls.map(url => () => fetch(url));
parallelWithLimit(fetchTasks, 3)
.then(responses => {
console.log('所有请求完成');
});
javascript复制// 流式处理大数据
async function processLargeDataset(dataset) {
const batchSize = 1000;
let processed = 0;
while (processed < dataset.length) {
const batch = dataset.slice(processed, processed + batchSize);
// 释放事件循环处理其他任务
await new Promise(resolve => setImmediate(resolve));
await processBatch(batch);
processed += batchSize;
// 手动触发垃圾回收(仅用于调试)
if (global.gc) {
global.gc();
}
}
}
主线程代码:
javascript复制// worker-manager.js
const workerPool = new Map();
function getWorker(key, workerPath) {
if (!workerPool.has(key)) {
const worker = new Worker(workerPath);
workerPool.set(key, worker);
}
return workerPool.get(key);
}
async function runInWorker(key, workerPath, data) {
const worker = getWorker(key, workerPath);
return new Promise((resolve, reject) => {
const messageId = Math.random().toString(36).slice(2);
const handler = ({ data: response }) => {
if (response.id === messageId) {
worker.removeEventListener('message', handler);
if (response.error) {
reject(new Error(response.error));
} else {
resolve(response.result);
}
}
};
worker.addEventListener('message', handler);
worker.postMessage({ id: messageId, data });
});
}
// 使用示例
async function processImageInWorker(imageData) {
try {
return await runInWorker(
'image-processor',
'./image-worker.js',
imageData
);
} catch (error) {
console.error('Worker处理失败:', error);
throw error;
}
}
Worker代码:
javascript复制// image-worker.js
self.addEventListener('message', async ({ data }) => {
try {
const result = await processImage(data.data);
self.postMessage({
id: data.id,
result
});
} catch (error) {
self.postMessage({
id: data.id,
error: error.message
});
}
});
async function processImage(imageData) {
// 使用OffscreenCanvas处理图像
const canvas = new OffscreenCanvas(imageData.width, imageData.height);
const ctx = canvas.getContext('2d');
// 复杂的图像处理逻辑
// ...
return canvas.transferToImageBitmap();
}
异步加载Wasm模块:
javascript复制async function initWasm() {
const imports = {
env: {
// 导入必要的函数
memory: new WebAssembly.Memory({ initial: 256 }),
abort: (msg) => console.error('Wasm abort:', msg)
}
};
// 并行获取和编译
const [response, wasmModule] = await Promise.all([
fetch('compute.wasm'),
WebAssembly.compileStreaming(fetch('compute.wasm'))
]);
const instance = await WebAssembly.instantiate(wasmModule, imports);
return {
instance,
memory: imports.env.memory
};
}
// 使用示例
let wasmCache;
async function heavyComputation(input) {
if (!wasmCache) {
wasmCache = await initWasm();
}
// 准备输入数据
const inputPtr = wasmCache.instance.exports.alloc(input.length);
new Uint8Array(wasmCache.memory.buffer)
.set(input, inputPtr);
// 执行计算
const resultPtr = wasmCache.instance.exports.compute(inputPtr, input.length);
// 处理结果
const result = new Uint8Array(wasmCache.memory.buffer)
.slice(resultPtr, resultPtr + input.length);
// 释放内存
wasmCache.instance.exports.free(inputPtr);
wasmCache.instance.exports.free(resultPtr);
return result;
}
React SSR流式渲染:
javascript复制import { renderToPipeableStream } from 'react-dom/server';
import { createServer } from 'http';
import { Suspense } from 'react';
async function handleRequest(req, res) {
// 预加载关键数据
const criticalData = await fetchCriticalData();
// 开始流式渲染
const { pipe } = renderToPipeableStream(
<App criticalData={criticalData} />,
{
bootstrapScripts: ['/main.js'],
onShellReady() {
res.statusCode = 200;
res.setHeader('Content-type', 'text/html');
pipe(res);
},
onError(error) {
console.error('渲染错误:', error);
res.statusCode = 500;
res.end('<!doctype html><h1>服务器错误</h1>');
}
}
);
}
// 组件中使用
function App({ criticalData }) {
return (
<html>
<head>
<title>流式SSR示例</title>
</head>
<body>
<Header />
<main>
<Suspense fallback={<div>加载中...</div>}>
<AsyncContent />
</Suspense>
</main>
<Footer criticalData={criticalData} />
</body>
</html>
);
}
async function AsyncContent() {
// 非关键数据延迟加载
const lazyData = await fetchLazyData();
return <div>{lazyData}</div>;
}
javascript复制// command-handler.js
class CommandHandler {
constructor(eventStore) {
this.eventStore = eventStore;
}
async handle(command) {
const event = await this.executeCommand(command);
await this.eventStore.append(event);
return event;
}
async executeCommand(command) {
// 验证命令
await validateCommand(command);
// 执行业务逻辑
const result = await businessLogic(command);
// 生成事件
return createEvent(command, result);
}
}
// query-handler.js
class QueryHandler {
constructor(readModel) {
this.readModel = readModel;
}
async handle(query) {
// 从读模型获取数据
return this.readModel.execute(query);
}
}
// event-store.js
class EventStore {
constructor() {
this.events = [];
this.subscribers = [];
}
async append(event) {
this.events.push(event);
await this.notifySubscribers(event);
}
async notifySubscribers(event) {
await Promise.all(
this.subscribers.map(sub => sub.handle(event))
);
}
}
// read-model-updater.js
class ReadModelUpdater {
constructor(database) {
this.database = database;
}
async handle(event) {
switch (event.type) {
case 'USER_CREATED':
await this.database.insert('users', event.payload);
break;
case 'USER_UPDATED':
await this.database.update('users', event.payload.id, event.payload);
break;
// 其他事件处理...
}
}
}
基于消息队列的模式:
javascript复制// producer.js
class MessageProducer {
constructor(queue) {
this.queue = queue;
}
async publish(eventType, payload) {
const message = {
id: generateId(),
type: eventType,
timestamp: Date.now(),
payload
};
await this.queue.send(message);
return message.id;
}
}
// consumer.js
class MessageConsumer {
constructor(queue, handlers) {
this.queue = queue;
this.handlers = handlers;
this.isRunning = false;
}
async start() {
this.isRunning = true;
while (this.isRunning) {
const message = await this.queue.receive();
try {
const handler = this.handlers[message.type];
if (handler) {
await handler(message.payload);
}
await this.queue.ack(message.id);
} catch (error) {
console.error('处理消息失败:', error);
await this.queue.nack(message.id);
}
}
}
stop() {
this.isRunning = false;
}
}
// 使用示例
const queue = new RabbitMQQueue('amqp://localhost');
const producer = new MessageProducer(queue);
const handlers = {
'order.created': async (order) => {
await processNewOrder(order);
},
'payment.received': async (payment) => {
await updateOrderStatus(payment.orderId, 'paid');
}
};
const consumer = new MessageConsumer(queue, handlers);
consumer.start().catch(err => {
console.error('消费者异常终止:', err);
process.exit(1);
});
// 发送消息示例
await producer.publish('order.created', {
id: 'order-123',
items: [/*...*/]
});
React数据加载策略:
javascript复制// use-async-resource.js
import { useState, useEffect, useRef } from 'react';
export function useAsyncResource(fetchFn, initialData = null) {
const [data, setData] = useState(initialData);
const [error, setError] = useState(null);
const [version, setVersion] = useState(0);
const abortControllerRef = useRef(null);
const refresh = () => {
setVersion(v => v + 1);
};
useEffect(() => {
const abortController = new AbortController();
abortControllerRef.current = abortController;
setError(null);
fetchFn({ signal: abortController.signal })
.then(result => {
if (!abortController.signal.aborted) {
setData(result);
}
})
.catch(err => {
if (!abortController.signal.aborted && err.name !== 'AbortError') {
setError(err);
}
});
return () => {
abortController.abort();
};
}, [fetchFn, version]);
return { data, error, refresh, isLoading: !data && !error };
}
// 组件中使用
function UserProfile({ userId }) {
const { data: user, error, refresh } = useAsyncResource(
({ signal }) => fetchUser(userId, { signal })
);
if (error) {
return <ErrorView error={error} onRetry={refresh} />;
}
if (!user) {
return <LoadingSpinner />;
}
return (
<div>
<h1>{user.name}</h1>
<button onClick={refresh}>刷新</button>
{/* 渲染用户详情 */}
</div>
);
}
NestJS异步控制器:
typescript复制// user.controller.ts
import { Controller, Get, Post, Body, UseInterceptors } from '@nestjs/common';
import { UserService } from './user.service';
import { TimeoutInterceptor } from '../common/interceptors/timeout.interceptor';
import { CacheInterceptor } from '../common/interceptors/cache.interceptor';
@Controller('users')
@UseInterceptors(TimeoutInterceptor)
export class UserController {
constructor(private readonly userService: UserService) {}
@Post()
async create(@Body()