前端面试官最爱:Promise

1,044 阅读3分钟

「这是我参与2022首次更文挑战的第7天,活动详情查看:2022首次更文挑战」。

现在的前端面试基本上都会问Promise,多数面试者也能应付自如。问题无非围绕以下这几个问题

  1. 是什么
  2. 解决了什么问题
  3. 怎么用

什么是Promise

Promise是一个对象,它代表了一个异步操作的最终完成或者失败。

Promise解决了什么问题

Promise解决了回调嵌套引发的回调地狱问题。

回调地狱示例: 看起来像是一个正常一部代码,对于一两个嵌套调用,看起来没问题,但是对于连续嵌套,代码的层级会越来越深,这种情况就被称为回调地狱或者事末日金字塔(把代码竖起来就像一个金字塔)。

doSomething(function(result) {
  doSomethingElse(result, function(newResult) {
    doThirdThing(newResult, function(finalResult) {
      console.log('Got the final result: ' + finalResult);
    }, failureCallback);
  }, failureCallback);
}, failureCallback);

Promise使代码串联在一起,用.then属性有序输出。将回调绑定到返回的Promise上,形成了一个Promise链条。

doSomething().then(function(result) {
  return doSomethingElse(result);
})
.then(function(newResult) {
  return doThirdThing(newResult);
})
.then(function(finalResult) {
  console.log('Got the final result: ' + finalResult);
})
.catch(failureCallback);

then 里的参数是可选的,catch(failureCallback) 是 then(null, failureCallback) 的缩略形式。也可以用箭头函数来表示,示例:

doSomething()
.then(result => doSomethingElse(result))
.then(newResult => doThirdThing(newResult))
.then(finalResult => console.log(`Got the final result: ${finalResult}`))
.catch(failureCallback);

Promise的用法

Promise对象有两个特点。

1.对象的状态不受外界影响。Promise有三种状态:pending(进行中)、fulfilled(已成功)和rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。

2.一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。

示例:普通写法

var promise = new Promise(function(resolve, reject) {
// 自动调用 
// 可以做任何异步操作,
// А когда они завершатся — нужно вызвать одно из: 
// resolve(res) 成功 
// reject(erroy) 失败 
})

示例:箭头函数写法,等待3秒,打印hello。

const wait = time => new Promise((resolve) => setTimeout(resolve, time)); 
wait(3000).then(() => console.log('Hello!')); // 'Hello!'

async/await是什么?

async函数,就是Generator函数的语法糖,它建⽴在Promises上,并且与所有现有的基于Promise的API兼容。

  1. Async—声明⼀个异步函数(async function someName(){...})。
  2. ⾃动将常规函数转换成Promise,返回值也是⼀个Promise对象。
  3. 只有async函数内部的异步操作执⾏完,才会执⾏then⽅法指定的回调函数。
  4. 异步函数内部可以使⽤await。
  5. Await—暂停异步的功能执⾏(var result = await someAsyncCall();)。
  6. 放置在Promise调⽤之前,await强制其他代码等待,直到Promise完成并返回结果。
  7. 只能与Promise⼀起使⽤,不适⽤与回调。
  8. 只能在async函数内部使⽤。

async/await相⽐于Promise的优势?

  1. async/await代码读起来更加同步,Promise虽然摆脱了回调地狱,但是then的链式调⽤也会带来额外的阅读负担。
  2. Promise传递中间值⾮常麻烦,⽽async/await⼏乎是同步的写法,⾮常优雅。
  3. async/await错误处理友好,async/await可以⽤成熟的try/catch,Promise的错误捕获⾮常冗余。
  4. async/await调试友好,Promise的调试很差,由于没有代码块,你不能在⼀个返回表达式的箭头函数中设置断点,如果你在⼀个.then代码块中使⽤调试器的步进(step-over)功能,调试器并不会进⼊后续的.then代码块,因为调试器只能跟踪同步代码的『每⼀步』。