Angular2 将服务方法从回调更改为异步

Ben*_*Ben 4 javascript asynchronous angular-promise angular

我启动了一个简单的 Angular2 Electron 应用程序,并且有一个查询本地 SQL Server 数据库的服务方法。到目前为止一切正常。现在我试图获取对我的组件的服务数据库调用的结果并以某种方式显示它。

问题是查询逻辑写的比较多的是回调语法:

sql.query(sqlString, (err, result) => {
    ...
    callback(result);
    ...
});
Run Code Online (Sandbox Code Playgroud)

我很难重写它以返回承诺,因为结果始终位于查询命令函数的结果参数内。我的组件如下所示:

export class LinkDocRetriever {

  constructor(private myService: MyService) {  }

  results = "";

  loadMyData(id: number): void {

    let tcData = this.myService.getMyData();
    tcData.forEach(element => {
      this.results += element.FileName + " " +  "....\n";
    });

  };
}
Run Code Online (Sandbox Code Playgroud)

我的服务如下所示:

import { Injectable } from "@angular/core";
import * as sql from "mssql";

@Injectable()
export class MyService {

    getMyData():Array<MyItem> {

        let myData:Array<MyItem> = [];

        let config = {
            user: "sa",
            password: "xxx",
            server: "localhost",
            database: "mydb"
        };

        const pool1 = new sql.ConnectionPool(config, err => {

            if (err) {
                console.log("connect erro: " + err);
            }

            let q:string = `SELECT TOP 10 * FROM MyTable;`;

            let final = pool1.request()
            .query<MyItem>(q, (err, result) => {
                if (err) {
                    console.log("request err: " + err);
                }

                console.log("db result count: " + result.recordsets[0].length);
                result.recordsets[0].forEach(row => {
                    myData.push(row);
                });
            });
        });
        return myData;
    }
}
Run Code Online (Sandbox Code Playgroud)

我确实得到了结果,但组件从未看到它,因为它在结果返回之前就返回了。

我尝试在 ConnectionPool 函数中对查询调用执行等待,但收到一条错误,指出只能在异步函数中调用等待,即使我在该方法上设置了异步。mssql 包有一个Async/Await 部分,但是当我尝试时,该页面上的给定语法给出了错误。

知道如何使用承诺来写这个吗?

Ngh*_*ran 5

正如您所指出的,有 3 种方法来处理异步函数:使用回调、使用 Promise 和使用 Async/Await。我将尝试展示所有三种方法,但您应该了解 JavaScript 中的事件循环以及它如何处理异步函数。

打回来

从技术上讲,回调是处理异步函数的最快方法,但一开始会很混乱,如果使用不当,可能会产生所谓的回调地狱。回调地狱非常可怕,甚至有人为此创建了一个网站http://callbackhell.com/。

所以你的代码可以重写为:

export class LinkDocRetriever {

  constructor(private myService: MyService) {  }

  results = "";

  loadMyData(id: number): void {

    // call getMyData with a function as argument. Typically, the function takes error as the first argument 
    this.myService.getMyData(function (error, tcData) {
       if (error) {
         // Do something
       }

       tcData.forEach(element => {
         this.results += element.FileName + " " +  "....\n";
       });
    });
  };
}
Run Code Online (Sandbox Code Playgroud)

服务

import { Injectable } from "@angular/core";
import * as sql from "mssql";

@Injectable()
export class MyService {
    // Now getMyData takes a callback as an argument and returns nothing
    getMyData(cb) {

        let myData = [];

        let config = {
            user: "sa",
            password: "xxx",
            server: "localhost",
            database: "mydb"
        };

        const pool1 = new sql.ConnectionPool(function(config, err) {

            if (err) {
                // Error occured, evoke callback
                return cb(error);
            }

            let q:string = `SELECT TOP 10 * FROM MyTable;`;

            let final = pool1.request()
            .query<MyItem>(q, (err, result) => {
                if (err) {
                    console.log("request err: " + err);
                    // Error occured, evoke callback
                    return cb(error);
                }

                console.log("db result count: " + result.recordsets[0].length);
                result.recordsets[0].forEach(row => {
                    myData.push(row);
                });

                // Call the callback, no error occured no undefined comes first, then myData
                cb(undefined, myData);
            });

        });
    }
}
Run Code Online (Sandbox Code Playgroud)

承诺

Promise 是一个特殊的对象,它允许您控制异步函数并避免回调地狱,因为您不必使用嵌套回调,而只需使用一个级别then和catch函数。在这里阅读有关 Promise 的更多信息

成分

export class LinkDocRetriever {

  constructor(private myService: MyService) {  }

  results = "";

  loadMyData(id: number): void {
    this.myService.getMyData()
      .then((tcData) => {
         // Promise uses then function to control flow
         tcData.forEach((element) => {
           this.results += element.FileName + " " +  "....\n";
         });
      })
      .catch((error) => {
         // Handle error here
      });

  };
}
Run Code Online (Sandbox Code Playgroud)

服务

@Injectable()
export class MyService {
    // Now getMyData doesn't take any argument at all and return a Promise
    getMyData() {

        let myData = [];

        let config = {
            user: "sa",
            password: "xxx",
            server: "localhost",
            database: "mydb"
        };

        // This is what getMyData returns
        return new Promise(function (resolve, reject) {
            const pool1 = new sql.ConnectionPool((config, err) => {

                if (err) {
                    // If error occurs, reject Promise
                    reject(err)
                }

                let q = `SELECT TOP 10 * FROM MyTable;`;

                let final = pool1.request()
                  .query(q, (err, result) => {
                      if (err) {
                          // If error occurs, reject Promise
                          reject(err)
                      }

                      console.log("db result count: " + result.recordsets[0].length);
                      result.recordsets[0].forEach((row) => {
                          myData.push(row);
                      });

                      // 
                      resolve(myData);
                  });

            });
        })

    }
}
Run Code Online (Sandbox Code Playgroud)

异步/等待

引入 Async/await 是为了解决您在处理回调和 Promise 时遇到的困惑。在这里阅读有关异步/等待的更多信息

成分

export class LinkDocRetriever {

  constructor(private myService: MyService) {  }

  results = "";

  // Look. loadMyData now has to have async keyword before to use await. Beware, now loadMyData will return a Promise.
  async loadMyData(id) {

    // By using await, syntax will look very familiar now
    let tcData = await this.myService.getMyData(tcData);
    tcData.forEach((element) => {
      this.results += element.FileName + " " +  "....\n";
    });
  };
}
Run Code Online (Sandbox Code Playgroud)

Service 与Promise中的完全相同,因为Async/await是专门为了处理它们而创建的。

注意:我从你的代码中删除了一些 Typescript 功能,因为我更习惯普通 JS,但你应该能够编译它们,因为 Typescript 是 JS 的超集。