我保证不会再回电

2025-06-08

我保证不会再回电

介绍

处理 JavaScript 的异步特性可能非常具有挑战性且令人沮丧。回调长期以来一直是默认的处理方式。ES6 为我们提供了使用 Promise 的回调替代方案。从 Node.js 4 开始,Promise 就已原生支持。

那是什么?

Promise 是一种抽象概念,它允许函数返回一个名为promise的对象。Promise 是异步操作的最终结果。当异步操作尚未完成时,我们称 Promise 处于pending 状态。当操作成功完成时,Promise 处于fulfilled 状态。当操作失败时,Promise 处于rejection 状态。

构建承诺

在 ES6 中,你可以使用Promise构造函数创建一个 Promise。它接受一个带有两个参数的函数,通常称为resolverejectresolve是 Promise 完成时调用的函数,而reject则是 Promise 被拒绝时调用的函数。

让我们从一个返回 Promise 的函数开始。这个 Promise 一定会被实现。

const myPromise = () => {
  return new Promise( ( resolve, reject ) => {
  console.log('I promise!')
  resolve()
})
}

myPromise()
.then(() => {
  console.log('I made it!')
})

// I promise!
// I made it!
Enter fullscreen mode Exit fullscreen mode

myPromise返回一个 Promise。当我们调用函数时,该 Promise 处于pending 状态,既没有 fulfilled 也没有 rejected。我们打印出I promise!并调用resolve函数。then ()方法负责处理已 fulfilled 的 Promise。resolve ()调用会触发then()方法,然后我们打印出I made it! 。

现在让我们看看一个被拒绝的承诺:


const rejectedPromise = () => {
  return new Promise( ( resolve, reject ) => {
    console.log('I promise!')
    reject('You lied to me!!')
  })
}

rejectedPromise()
.then(() => {
  console.log('I made it!')
})
.catch(err => {
  console.log('How dare you?')
  console.log(err)
})

// I promise!
// How dare you?
// You lied to me!!

Enter fullscreen mode Exit fullscreen mode

这里,我们的 Promise 调用了Reject函数,这意味着我们的 Promise 被拒绝了。这会触发catch方法。调用Reject并返回错误信息是一个好习惯。请注意,在这种情况下, then()方法不会被调用。

我保证然后我保证然后我保证然后我保证然后......

Promise 最令人惊奇的地方在于它能够链式执行。如果我们以前面的例子为例,并添加一个then()

rejectedPromise()
.then(() => {
  console.log('I made it!')
})
.catch(err => {
  console.log('How dare you?')
  console.log(err)
})
.then(() => {
  console.log('I forgive you no matter what.')
})

//I promise!
//How dare you?
//You lied to me!!
//I forgive you no matter what.
Enter fullscreen mode Exit fullscreen mode

最后一个then()会一直运行。如果我们的 Promise 被满足了,第一个 then 会被执行,catch 会被跳过,最后最后一个 then 会被运行。

让我们创建三个承诺并将它们链接起来:


const promiseToLove = iAmMature => {
  return new Promise( ( resolve, reject ) => {
    if( iAmMature ){
      resolve('I love you so much!')
    } else {
      reject("It's not you, it's me...")
    }
  })
}

const promiseToProtect = iAmNice => {
  return new Promise( ( resolve, reject ) => {
    if( iAmNice ){
      resolve('I promise I will protect you!')
    } else {
      reject('What? Get lost!')
    }
  })
}

const promiseToBeHereOnTime = hairLooksGood => {
  return new Promise( ( resolve, reject ) => {
    if( hairLooksGood ){
      resolve('I promise I will be there!')
    } else {
      reject('How about tomorrow?')
    }
  })
}

//First promise
promiseToLove(true)
.then(statement => {
  console.log(statement)
})
.catch(statement => {
  console.log(statement)
})
//returns another promise
.then(() => promiseToProtect(true))
//handles our second promise
.then(statement => {
  console.log(statement)
})
.catch(statement => {
  console.log(statement)
})
// returns annother promise
.then(() => promiseToBeHereOnTime(true))
// handles our third promise
.then(statement => {
  console.log(statement)
})
.catch(statement => {
  console.log(statement)
})
// this always runs
.then(() => {
  console.log('And they lived happily ever after!!!')
})

// I love you so much!
// I promise I will protect you!
// I promise I will be there!
// And they lived happily ever after!!!

Enter fullscreen mode Exit fullscreen mode

这三个函数都接受一个参数(布尔值)。如果参数设置为 true,则 Promise 会被履行,否则会被拒绝。一旦一个 Promise 被满足,我们就会返回另一个 Promise 并处理它……

你能看出 Promise 在处理 JavaScript 的异步特性时有多优雅吗?无需嵌套无数个回调。它简洁美观。你可以想象一下,如果我们在这里使用回调而不是 Promise,代码会是什么样子。

只是为了好玩,让我们把一切都设置为假,因为有些人无法信守诺言......

//First promise
promiseToLove(false)
.then(statement => {
  console.log(statement)
})
.catch(statement => {
  console.log(statement)
})
//returns another promise
.then(() => promiseToProtect(false))
//handles our second promise
.then(statement => {
  console.log(statement)
})
.catch(statement => {
  console.log(statement)
})
// returns annother promise
.then(() => promiseToBeHereOnTime(false))
// handles our third promise
.then(statement => {
  console.log(statement)
})
.catch(statement => {
  console.log(statement)
})
// this always runs
.then(() => {
  console.log('Why are you like this?')
})

// It's not you, it's me...
// What? Get lost!
// How about tomorrow?
// Why are you like this?
Enter fullscreen mode Exit fullscreen mode

现实生活中的承诺

在 Node.js 中,并非所有函数都支持开箱即用的 Promise。为了解决这个问题,你可以使用util 模块中的promisify方法。它接受一个函数并将其转换为返回 Promise 的函数。

克隆文件

要克隆一个文件,我们需要读取其内容,然后将其写入新文件。回调风格的话,你会得到类似这样的代码:

const fs = require('fs')

fs.readFile('myFile.js', 'utf-8', (err, data) => {
  fs.writeFile('clone.js', data, err => {
    if(err){
      throw err
    } else {
      console.log('All done')
    }
  })
})
Enter fullscreen mode Exit fullscreen mode

好的,我们已经能看到远处回调地狱的大门了。让我们用 Promises 来实现它。我甚至会先写入一个文件,然后读取它,再写入一个新文件,最后读取我们新的克隆文件。是的,我知道,我疯了……

const fs = require('fs')

// Get the promisify method from the util module
const { promisify } = require('util')

// Promisify our readFile and writeFile function
const readFile = promisify(fs.readFile)
const writeFile = promisify(fs.writeFile)

writeFile('original.txt', 'Promise me you will clone me!')
.then(() => readFile('original.txt', 'utf-8'))
.then(content => writeFile('clone.txt', content))
.then(() => readFile('clone.txt', 'utf-8'))
.then(cloneContent => console.log(cloneContent))
.catch(err => console.log('Error occured:', err))


// Promise me you will clone me!
Enter fullscreen mode Exit fullscreen mode

是啊,这太有意思了。为什么还要用回调函数呢?我们的 writeFile 和 readFile 要么在resolve()被调用时返回文件内容,要么在rejection()被调用时返回错误信息。在我们的例子中,我只写了一个catch() 。但是,如果前面任何一个 Promise 被拒绝,这个catch()就会被调用:

writeFile('original.txt', 'Promise me you will clone me!')
.then(() => readFile('404NOTFOUND.txt', 'utf-8')) // <= Error here
.then(content => writeFile('clone.txt', content))
.then(() => readFile('clone.txt', 'utf-8'))
.then(cloneContent => console.log(cloneContent))
.catch(err => console.log('Error occured:', err)) // <= Trigger this


//Error occured: { Error: ENOENT: no such file or directory, open //'404NOTFOUND.txt'
//  errno: -2,
//  code: 'ENOENT',
//  syscall: 'open',
//  path: '404NOTFOUND.txt' }

Enter fullscreen mode Exit fullscreen mode

好了,这些应该足够你开始使用自己的 Promise 了。保持理智,让代码更简洁,使用 Promise 而不是回调 :)

鏂囩珷鏉ユ簮锛�https://dev.to/damcosset/i-promise-i-wont-callback-anymore-cp3
PREV
关于最终推出某项产品
NEXT
以太坊开发:入门