【发布时间】:2019-10-06 10:23:58
【问题描述】:
我的这个问题可能与角度概念无关。但我在角度的背景下遇到它。我点击 Angular 模板中的一个按钮,它会触发 onSubmit 函数。
scanFiles 是一个长时间运行的函数,它返回一个承诺。我希望 console.log("test1") 在长时间运行的功能启动之前打印出来。但这不会发生。它仅在长时间运行功能完成后打印出来。为什么会这样?
onSubmit(){
this.scanFiles(this.foldersPath).then((filesPath)=>{
//after scan finish
})
.catch((err)=>console.log(err))
console.log("test1")
}
2019 年 10 月 7 日进一步更新)
如下所示,我的 scanFiles 函数返回了一个承诺。因此,无论我的 promise 作业做什么,理论上,我认为应该在浏览器开始执行 promise 作业之前打印出“test1”。
scanFiles(foldersPath: any):Promise<string[]> {
return new Promise(
(resolveFn, rejectFn)=>{
try{
const scanResult:string[]= foldersPath.reduce(
(prevFolderPath:string[], currFolderPath:string)=> {
let files:string[] = this.fileService.getContentInDirectory (currFolderPath, this.filter.bind(this), this.getProcessDirectoryContentFn(), this.subfolderDepthInput)
prevFolderPath.push(...files)
return prevFolderPath
},new Array<string>())
console.log(scanResult)
resolveFn(scanResult)
}
catch(e){
console.log(e)
rejectFn(e)
}
}
)
``
2019 年 10 月 8 日更新 geContentInDirectory里面有一个readdirSync()函数
getContentInDirectory(dir:string, filterContentFn?:(fullPath:string)=>boolean, processContentFn?:(fullPath:any)=>string, maxSubFolderDepth?:number ): string[]{
let paths:string[]=[];
//const dir_NOT_A_DIRECTORY =
if(!dir || !fs.lstatSync(dir).isDirectory())
throw new Error("First Parameter must be a directory")
fs.readdirSync(dir).forEach(entityName=>{
let fullPath:string = path.join(dir, entityName)
let isFile = fs.lstatSync(fullPath).isFile()
if(maxSubFolderDepth==undefined || maxSubFolderDepth >= 0){
if(isFile){
if(filterContentFn) {
if(filterContentFn(fullPath)){
let content = processContentFn? processContentFn(fullPath): fullPath
paths.push(content)
}
}
}
else {
const depth = maxSubFolderDepth==undefined ? undefined: maxSubFolderDepth-1
paths.push(...this.getContentInDirectory(fullPath, filterContentFn, processContentFn, depth))
}
}
})
return paths;
}
}
2019 年 10 月 8 日更新
我通过这样重写我的代码做了一个实验: 结果是“test0”,“test2”,“test1”依次打印出来。
结论:当 Promise 对象被创建时,它在 Promise 对象中定义的长期运行的作业将被立即触发并执行。 一旦我的 scanFiles 完成了它的工作,“test0”就会被打印出来。
然后在 promise 的 then 函数中注册(注册但尚未执行)回调。然后打印test2"。线程会回到它的事件循环,发现它仍然需要处理回调函数,导致打印"test1"
let p= this.scanFiles(this.foldersPath)
console.log("test0")
p.then((filesPath)=>{
console.log("test1")
})
.catch((err)=>console.log(err))
console.log("test2")
感谢 Tomalak 用户的解决方案和解释,这导致了我的上述理解。
回答我的问题:我的 Promise 对象内部包含同步任务。难怪我的“test1”(参考我最上面的代码)只有在promise对象中的任务完成后才会打印出来。
【问题讨论】:
-
在所示的sn-p中,执行顺序是这样的:1)
this.scanFiles()被调用,2).then()回调被注册,3).catch()回调被注册,4)console.log()被调用。如果scanFiles确实没有阻塞,则在处理scanFiles结果之前调用console.log()。如果不是这样,那么scanFiles不会按照您的要求行事。 -
换句话说,如果没有看到
scanFiles的正文,就无法说出发生了什么。 -
您好,感谢您的回复。我添加了 scanFiles 的正文
-
...问题就出来了。
getContentInDirectory使用fs.readdirSync和fs.lstatSync,这意味着它将阻塞代码。重写它,使其使用fs.readdir和fs.lstat并返回一个promise。然后,您可以一直将该承诺返回到scanFiles的结果。如果您在节点 10.0.0+ 上,您可以使用fsPromises而不是fs让您的生活更轻松。 -
“我以为线程会创建一个新的 Promise(),并将其返回给调用者”。不,这不是这样的。
new Promise()立即 调用它的有效载荷函数。并且在有效载荷函数返回时返回。如果有效负载功能阻塞 10 秒(因为它是同步的,就像您的情况一样),那么new Promise()调用将需要 10 秒。 Promise 确实适用于同步代码,但它们并不会神奇地让这个事实消失。您需要一直使用异步代码才能获得任何好处。
标签: angular asynchronous promise