在诺言完成其工作之前,代码不会执行

时间:2019-10-06 10:23:58

标签: angular asynchronous promise

这个问题我可能与角度概念没有任何关系。但是我在有角度的背景下遇到了它。我单击角度模板中的按钮,它会触发onSubmit函数。

scanFiles是一个长时间运行的函数,它返回一个Promise。我希望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。因此,无论我的诺言工作做什么,从理论上讲,我认为在浏览器开始执行诺言工作之前,应先打印出“ 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”。

结论:创建诺言对象时,其在诺言对象中定义的长期运行的作业将被触发并立即执行。 我的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对象内部包含同步任务。难怪只有在promise对象中的任务完成后,我的“ test1”(指的是我的最高代码)才被打印出来。

2 个答案:

答案 0 :(得分:1)

据我对您当前代码的理解,这是getContentInDirectory的完全异步,应许返回的版本。为了进行测试,我发现TypeScript太吵了,将其删除,然后根据需要添加回去。

function getContentInDirectory(dir, filterContentFn, processContentFn, maxSubFolderDepth) {
    return new Promise((resolve, reject) => {
        let result = [], pending = 0;
        function worker(currPath, depth) {
            pending++;
            fs.lstat(currPath, (err, stat) => {
                pending--;
                if (err) return reject(err);
                if (stat.isDirectory()) {
                    if (depth >= 0) {
                        pending++;
                        fs.readdir(currPath, (err, children) => {
                            pending--;
                            if (err) return reject(err);
                            children.forEach(name => worker(path.join(currPath, name), depth - 1));
                        });
                    }
                } else if (!filterContentFn || filterContentFn(currPath)) {
                    result.push(processContentFn ? processContentFn(currPath) : currPath);
                }
                if (!pending) resolve(result);
            });
        }
        worker(dir, maxSubFolderDepth >= 0 ? maxSubFolderDepth : Infinity);
    });
}

有多种方法可以实现此目的,包括使用fs Promises API,它从节点版本10开始可用,但至今仍标记为“实验性”。上面没有做任何假设,并且适用于任何节点版本。

现在您可以本着以下精神在scanFiles中使用它:

function scanFiles(foldersPath) {
    let pendingPaths = foldersPath.map(currFolderPath => {
        return getContentInDirectory(currFolderPath, filterFunc, processFunc, depth);
    });
    return Promise.all(pendingPaths).then(results => {
        return Array.prototype.concat.apply([], results);  // flatten
    });
}

最后在您的事件处理程序中:

onSubmit(){
    this.scanFiles(this.foldersPath).then(filesPath => {
        // after scan finish 
        console.log("this prints last");
    })
    .catch(err => console.log(err));
    console.log("this prints first");
}

这里要注意的是,如果您想使自己使用的功能也要异步,那么从一开始就需要进行实际工作的功能是异步的。从节点fs功能的“同步”版本切换到常规的异步对应版本是至关重要的步骤。

当然,此更改意味着程序中每个getContentInDirectory的使用者都必须更改为异步代码。

答案 1 :(得分:0)

您可以将多个然后链接在一起:

new Promise(function(resolve, reject) {

  setTimeout(() => resolve(1), 1000); // (*)

}).then(function(result) { // (**)

  alert(result); // 1
  return result * 2;

}).then(function(result) { // (***)

  alert(result); // 2
  return result * 2;

}).then(function(result) {

  alert(result); // 4
  return result * 2;

});

想法是将结果通过.then处理程序链传递。

这里的流程是:

最初的承诺在1秒内解决(), 然后,.then处理程序称为()。 它返回的值将传递到下一个.then处理程序( ) …等等。

链接Promises chaining

或者您可以在最终执行后执行一些操作,最后将其写入:

let isLoading = true;

fetch(myRequest).then(function(response) {
    var contentType = response.headers.get("content-type");
    if(contentType && contentType.includes("application/json")) {
      return response.json();
    }
    throw new TypeError("Oops, we haven't got JSON!");
  })
  .then(function(json) { /* process your JSON further */ })
  .catch(function(error) { console.error(error); /* this line can also throw, e.g. when console = {} */ })
  .finally(function() { isLoading = false; });
相关问题