是的,我认为您必须更改代码以使用 async 模式,否则您将阻塞您想要同时呈现进度对话框的 UI 线程。
这里是使用spawn和spawnSync的区别:
async方法中如何从子进程输出中读取的例子在这里:
let childProcess = spawn(someProcessToSpawn)
.on("close", (code, signal) => {
console.log(`Closed: ${code} ${signal}`);
if (childProcess.killed) { console.log('KILLED'); }
resolve();
clearInterval(interval);
})
.on("error", err => {
reject(err);
});
childProcess.stdout
.on("data", (chunk: string | Buffer) => {
// YOUR CODE GOES HERE
console.log(`stdout: ${chunk}`);
progressUpdate = chunk.toString('utf8', 0, 50).replace(/[\r\n]/g, '');
});
如果要运行整个示例,请克隆 progress-sample,运行 npm install 并将 extension.ts 内容替换为以下代码:
'use strict';
import { ExtensionContext, window, commands, ProgressLocation, CancellationToken, Progress } from 'vscode';
import { spawn, spawnSync } from 'child_process';
export function activate(context: ExtensionContext) {
context.subscriptions.push(commands.registerCommand('extension.startTask', async () => {
let mode = await window.showQuickPick(['sync', 'async'], { placeHolder: 'Pick mode...' });
window.withProgress({
location: ProgressLocation.Notification,
title: "I am long running",
cancellable: true
}, async (progress, token) => {
token.onCancellationRequested(() => {
console.log("User canceled the long running operation");
});
switch (mode) {
case undefined:
return; // canceled by the user
case 'sync':
return spawnSomethingSync(token);
case 'async':
default:
return spawnSomethingAsync(progress, token);
}
});
}));
}
/**
* Synchronous approach
* @param _token cancellation token (unused in the sync approach)
*/
function spawnSomethingSync(_token: CancellationToken): Promise<void> {
return new Promise(resolve => {
console.log('Started...');
let child = spawnSync('cmd', ['/c', 'dir', '/S'], { cwd: 'c:\\', encoding: 'utf8' });
console.log(`stdout: ${child.stdout.slice(0, 1000)}`); // otherwise it is too big for the console
resolve();
});
}
/**
* Asynchronous approach
* @param token cancellation token (triggered by the cancel button on the UI)
*/
function spawnSomethingAsync(progress: Progress<{ message?: string; increment?: number }>, token: CancellationToken): Promise<void> {
return new Promise<void>((resolve, reject) => {
if (token.isCancellationRequested) {
return;
}
var progressUpdate = 'Starting up...';
const interval = setInterval(() => progress.report({ message: progressUpdate }), 500);
let childProcess = spawn('cmd', ['/c', 'dir', '/S'], { cwd: 'c:\\' })
.on("close", (code, signal) => {
console.log(`Closed: ${code} ${signal}`);
if (childProcess.killed) { console.log('KILLED'); }
resolve();
clearInterval(interval);
})
.on("error", err => {
reject(err);
});
childProcess.stdout
.on("data", (chunk: string | Buffer) => {
// YOUR CODE GOES HERE
console.log(`stdout: ${chunk}`);
progressUpdate = chunk.toString('utf8', 0, 50).replace(/[\r\n]/g, '');
});
token.onCancellationRequested(_ => childProcess.kill());
});
}
如果您不在 Windows 上,只需将 dir /S 和 c:\\ 替换为其他适当的长时间运行命令即可。
使用 async 方法的另一个好处是可以轻松连接取消按钮以终止生成的进程:
token.onCancellationRequested(_ => childProcess.kill());
...您还可以通过更新progress 对象来显示进度。