理解JavaScript之Promise

在JavaScript的世界中,所有代码都是单线程执行的。由于这个“缺陷”,导致JavaScript的所有网络操作,浏览器事件,都必须是异步执行。

Promise 是异步编程的一种解决方案: 从语法上讲,promise是一个对象,从它可以获取异步操作的消息;从本意上讲,它是承诺,承诺它过一段时间会给你一个结果。 promise有三种状态:pending(等待态),fulfiled(成功态),rejected(失败态);状态一旦改变,就不会再变。创造promise实例后,它会立即执行。

作用:

  1. promise是一个对象,对象和函数的区别就是对象可以保存状态,函数不可以(闭包除外)
  2. 并未剥夺函数return的能力,因此无需层层传递callback,进行回调获取数据
  3. 代码风格,容易理解,便于维护
  4. 多个异步等待合并便于解决

三种状态

  1. pending[等待]初始状态
  2. resolved(又称 fulfilled)[成功]操作成功
  3. rejected[失败]操作失败

当promise状态发生改变,就会触发then()里的响应函数处理后续步骤;

promise状态一经改变,不会再变。

基本的 api

  • Promise.resolve() 

  • Promise.reject() 

  • Promise.prototype.then() 

  • Promise.prototype.catch()

  • Promise.all() // 所有的完成

  • Promise.race() // 竞速,完成一个即可

一个最简单的Promise例子:生成一个0-2之间的随机数,如果小于1,则等待一段时间后返回成功,否则返回失败:

new Promise(function (resolve, reject) {
    let timeOut = Math.random() * 2;

    setTimeout(function () {
        if (timeOut < 1) {
            console.log('call resolve()...');
            resolve('200 OK');
        }
        else {
            console.log('call reject()...');
            reject('timeout in ' + timeOut + ' seconds.');
        }
    }, timeOut * 1000);

}).then(function (r) {
    console.log('Done: ' + r);
}).catch(function (reason) {
    console.log('Failed: ' + reason);
});

Promise.All()

Promise.all() 方法返回一个 Promise 实例,Promise.all()可以将多个Promise实例包装成一个新的Promise实例。同时,成功和失败的返回值是不同的,成功的时候返回的是一个结果数组,而失败的时候则返回最先被reject失败状态的值。

let p1 = new Promise((resolve, reject) => {
    resolve('成功了')
  })
  
  let p2 = new Promise((resolve, reject) => {
    resolve('success')
  })
  
  let p3 = new Promise((resolve, reject) => {
    reject('失败了')
  })

  let p4 = new Promise((resolve, reject) => {
    reject('failed')
  })
  
  
  Promise.all([p1, p2]).then((result) => {
    console.log(result)
  }).catch((error) => {
    console.log(error)
  })
  
  Promise.all([p1,p3,p2,p4]).then((result) => {
    console.log(result)
  }).catch((error) => {
    console.log(error)
  })

// Array(2) ["成功了", "success"]
// 失败了

PS:Promise.all()获得的成功结果的数组里面的数据顺序和Promise.all接收到的数组顺序是一致的,即p1的结果在前,即便p1的结果获取的比p2要晚。

Promise.race()

Promse.race()就是赛跑的意思,意思就是说,Promise.race([p1, p2, p3])里面哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。

let p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve('success')
  },1000)
})

let p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject('failed')
  }, 500)
})

Promise.race([p1, p2]).then((result) => {
  console.log(result)
}).catch((error) => {
  console.log(error)  
})

// failed

几道面试题

题目一:

const promise = new Promise((resolve, reject) => {
  console.log(1)
  resolve()
  console.log(2)
})
promise.then(() => {
  console.log(3)
})
console.log(4)

//1 2 4 3

Promise 构造函数是同步执行的,promise.then 中的函数是异步执行的。

题目二:

const promise = new Promise((resolve, reject) => {
  resolve('success1')
  reject('error')
  resolve('success2')
})

promise
  .then((res) => {
    console.log('then: ', res)
  })
  .catch((err) => {
    console.log('catch: ', err)
  })

// then: success1

构造函数中的 resolve 或 reject 只有第一次执行有效,多次调用没有任何作用,呼应代码二结论:promise 状态一旦改变则不能再变。

题目三:

Promise.resolve(1)
  .then((res) => {
    console.log(res)
    return 2
  })
  .catch((err) => {
    return 3
  })
  .then((res) => {
    console.log(res)
  })

//1 2

promise 链式调用。提起链式调用我们通常会想到通过 return this 实现,不过 Promise 并不是这样实现的。promise 每次调用 .then 或者 .catch 都会返回一个新的 promise,从而实现了链式调用。

题目四:

const promise = new Promise((resolve, reject) => {
  setTimeout(() => {
    console.log('once')
    resolve('success')
  }, 1000)
})

const start = Date.now()
promise.then((res) => {
  console.log(res, Date.now() - start)
})
promise.then((res) => {
  console.log(res, Date.now() - start)
})

// once 
// success 1026
// success 1027

promise 的 .then 或者 .catch 可以被调用多次,但这里 Promise 构造函数只执行一次。或者说 promise 内部状态一经改变,并且有了一个值,那么后续每次调用 .then 或者 .catch 都会直接拿到该值。

​​​​​​​题目五:

const promise = Promise.resolve()
  .then(() => {
    return promise
  })
promise.catch(console.error)

//TypeError: Chaining cycle detected for promise #<Promise>

.then 或 .catch 返回的值不能是 promise 本身,否则会造成死循环。

​​​​​​​题目六:

Promise.resolve(1)
  .then(2)
  .then(Promise.resolve(3))
  .then(console.log)

// 1

.then 或者 .catch 的参数期望是函数,传入非函数则会发生值穿透。

​​​​​​​题目七:

process.nextTick(() => {
  console.log('nextTick')
})
Promise.resolve()
  .then(() => {
    console.log('then')
  })
setImmediate(() => {
  console.log('setImmediate')
})
console.log('end')

// end
// nextTick
// then
// setImmediate

process.nextTickpromise.then 都属于 microtask,而 setImmediate 属于 macrotask,在事件循环的 check 阶段执行。事件循环的每个阶段(macrotask)之间都会执行 microtask,事件循环的开始会先执行一次 microtask。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值