0

I moving from callback style to promise style to avoid the callback that made me hard to maintain the code. I use util module to import and convert the callback function with util.promisify(), but the question is if the function have at least two callbacks. How the function will return in promise form, then how can I manage the code to get the promise from two conditional callback.

Callback style example.

loginAccount = async ({email,password},success,unsuccess) => {
    this.db.query(`SELECT id, password FROM account WHERE email = ?`,[email],(err,result)=>{
      if (err) throw err
      if (result.length === 0) {
        unsuccess("ไม่มีบัญชีนี้อยู่ในระบบ ตรวจสอบอีเมลว่าถูกต้องหรือไม่")
        return
      }
      const account = result[0]
      bcrypt.compare(password,account.password,(error,response)=>{
        if (err) throw err
        if (!(response)) {
          unsuccess("รหัสผ่านไม่ถูกต้อง")
          return
        }
        this.getUser(account.id,success,unsuccess)
      })
    })
  }

I can convert into promise form like this, but I don't know how to return success or unsuccess.

  loginAccount = async ({email,password},success,unsuccess) => {
    const result = await this.query(`SELECT id, password FROM account WHERE email = ?`,email)
    if (result.length > 0) {
      const account = result[0]
      const response = await this.compareHash(password,account.password)
      if (response) {
        return this.getUser(account.id)
      }
      else {
        // return unsuccess
        // Wrong password
      }
    }
    else {
      // return unsuccess
      // No account in the database
    }
}



another example of callback style and promise style.

registerAccount = async ({firstname,lastname,email,password},success,unsuccess) => {
    this.hasAccount(email,(exist)=>{
      if (exist === true) {
        unsuccess("บัญชีนี้เคยลงทะเบียนไว้แล้ว")
        return
      }
      bcrypt.hash(password,this.saltRound,(err,hash)=>{
        if (err) throw err
        this.db.query(`INSERT INTO account (firstname,lastname,email,password) VALUES(?,?,?,?); 
                      SET @user_id = LAST_INSERT_ID(); INSERT IGNORE INTO role (id) VALUES (@user_id);
                      INSERT INTO student_data (id) VALUES (@user_id); 
                      INSERT INTO profile_image (id) VALUES (@user_id);`,
        [firstname,lastname,email,hash],
        (err,result)=>{
          if (err) throw err
          success()
        })
      })
    })
  }
registerAccount = async ({firstname,lastname,email,password},success,unsuccess) => {
    const exist = await this.hasAccount(email)
    if (exist) {
      // unsuccess function return
      // account is already exists.
      return
    }
    const hash = await this.generateHash(password,this.saltRound)
    const registerQuery = `INSERT INTO account (firstname,lastname,email,password) VALUES(?,?,?,?); 
                          SET @user_id = LAST_INSERT_ID(); INSERT IGNORE INTO role (id) VALUES (@user_id);
                          INSERT INTO student_data (id) VALUES (@user_id); 
                          INSERT INTO profile_image (id) VALUES (@user_id);`
    const result = await this.query(registerQuery,[firstname,lastname,email,hash])
    
    // success function
  }

this.query, this.compareHash and this.generateHash, I'm using the library called util.promisify() to get promise function.

this.query come from mysql query function, this.compareHash and this.generateHash both are come from bcrypt.compare and brcypt.hash

this.compareHash will compare the hash by using plain password and hash password.

this.generataHash will generate the hash by using plain password and salt round to return a hash password.

this.getUser function.

  getUser = async (id) => {
    const result = await this.query(`SELECT * FROM profile_view WHERE id = ?`,id)
    return result[0]
  }
  • When promisifying the method, you **remove** both of the `success` and `unsuccess` callbacks. You only fulfill (by `return`ing) or reject (by `throw`ing) the promise created by the `async` function. – Bergi Apr 09 '22 at 05:48
  • Please show `this.query`, `this.compareHash` and `this.getUser` definitions – Bergi Apr 09 '22 at 05:49
  • @Bergi I'm showed these definition by editing the post. – Paweenwat Maneechai Apr 09 '22 at 05:57
  • Ah, that's where you were using `util.promisify`, I was wondering already where you were using that after mentioning it. – Bergi Apr 09 '22 at 06:00
  • Sorry,I forgot to add a defintion of `this.getUser`,but now I'm added. – Paweenwat Maneechai Apr 09 '22 at 06:05
  • 1
    As Bergi says, the notion of 'multiple callbacks' disappears and the Promise either succeeds or it doesn't. How you decide what type of success or failure is up to you e.g. `reject('wrong password')` / `reject('user unknown')` etc. – James Apr 09 '22 at 06:22
  • @James If it possible that using `resolve()` and `reject()` without creating `new Promise()`? – Paweenwat Maneechai Apr 09 '22 at 06:26
  • 1
    @PaweenwatManeechai No, you can't call `resolve()` or `reject()`. As I wrote, in an `async function`, you simply `throw` the error or `return` the result. – Bergi Apr 09 '22 at 06:57
  • @PaweenwatManeechai no because those callbacks don't exist with `async` / `await`, but the process is still the same i.e. `return 'wrong password'` / `return 'user known'` or whatever logical flow makes sense to your application. – James Apr 09 '22 at 08:32
  • I don't know that is it correct to use without `promise` I'm using `return` for return the value like `resolve` and `throw Error("Some Message")` like `reject` and using `try/catch`. – Paweenwat Maneechai Apr 09 '22 at 08:57
  • @PaweenwatManeechai Well, now you know that it is correct, even if you didn't know before. – Bergi Apr 09 '22 at 12:30
  • @PaweenwatManeechai it's _still_ a Promise, you just aren't explicitly creating it when you use `async` / `await`, it's all done under the hood for you (`return` = `resolve`, `throw` = `reject`). – James Apr 10 '22 at 09:10

0 Answers0