重学ES6(六)Promise

213 阅读4分钟

Promise 对象用于表示一个异步操作的最终完成(或失败),及其结果值。ES6语法中,Promise 是一个对象,从它可以获取异步操作的消息。

Promise对象有以下两个特点:

  • 对象的状态不受外界影响。Promise 对象代表一个异步操作,有三种状态: pending(进行中)、fulfilled(已成功)、rejected(已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。
  • 一旦状态改变,就不会再变,任何时候都可以得到这个结果。Promise对象的状态改变,只有两种情况:从pending变为fulfilled和从pending变为rejected。只要这两种情况繁盛,状态就凝固了,不会再变了。你再对Promise对象添加回调函数,也会立即得到这个结果。这与事件(event)完全不同,事件的特点是,如果你错过了,再去监听,是得不到结果的。

eg1:

const promise = new Promise(function(resolve, reject) {
	// do something
    if (异步操作成功) {
    	resolve(value)
    } else {
    	reject(error)
    }
})

上面代码是生成一个Promise对象的实例,可以用the来指定得到resolved 和 rejected 后的回调。

promise.then(function(val){
	// success
}, function(error){
	// failure
})

下面是一个异步方法再 Promise 内部的例子

function timeout (ms) {
  return new Promise((resolve, reject) => {
  // 定时器定时执行
  setTimeout(() => {
      resolve('done')
    }, ms)
  })
}

timeout(1000).then(value => {
  console.log(value)
})

Promise 新建后,就会立即执行,只是resolve根据内部调用方法设置抛出

let promise = new Promise((resolve, reject) => {
	console.log('Promise')
    resolve()
})

promise.then(function() {
	console.log('resolved')
})

console.log('Hi!')

// Promise
// Hi
// resolved

上面代码中,Promise创建后,就立即执行,输出Promise。then是resolve之后的回调函数,将再当前脚本同步任务执行完成后才会执行。

Promise 异步加载图片

function loadImageAsync(url) {
	return new Promise((resolve, reject) => {
    	const image = new Image()
        image.src = url
        
        image.onload = function() {
        	resolve(image)
        }
        image.onerror = function() {
        	reject(new Error('Could not load image'))
        }
    })
}

Promise 实现 Ajax 操作

const getJson = function(url) {
	const promise = new Promise((resolve, reject) => {
    	let xhr = new XMLHttpRequest();
  		xhr.open('GET', url, true);
  		xhr.send();
        xhr.onreadystatechange = function () {
            // 这步为判断服务器是否正确响应
          if (xhr.readyState == 4 && xhr.status == 200) {
            console.log(xhr.responseText);
            resolve(xhr.responseText)
          }
        }
    })
    
    return promise
}
getJson("/post.json").then(function(json) {
	console.log(json)
}, function(error) {
	console.log(error)
})

如果调用 resolve 函数和 reject 函数时带有参数,那么它们的参数会被传递给回调函数。

const p1 = new Promise((resolve, reject) => {
	// ...
})
const p2 = new Promise((resolve, reject) => {
	// ...
	resolve(p1)
})

上面代码中,p1和p2都是 Promise的实例,但是 p2 的 resolve 方法将 p1作为参数,即一个异步操作的结果是返回另一个异步操作。

const p1 = new Promise(function (resolve, reject) {
  setTimeout(() => {
      reject(console.log('---fail----'))
  }, 1000)
})

const p2 = new Promise(function (resolve, reject) {
  setTimeout(() => {
      resolve(p1)
  }, 1000)
})

p2
  .then(result => console.log(result))
  .catch(error => console.log(error))
 
// ---fail----

Promise.prototype 上的方法

  • Promise.prototype.then()
// then 返回一个新的 Promise 实例
const promise = new Promise((resolve, reject) => {
	setTimeout(() => {
    	return resolve('chencc')
    }, 1000)
})

promise.then(resolve => {
    console.log(resolve)
	return 'abc'
}).then(result => {
	console.log(result)
})

// 'chencc'
// 'abc'
  • Promise.prototype.catch()
// Promise.prototype.catch()
// .then(null, rejection)
// .then(undefined, rejection)

const promise = new Promise((resolve, reject) => {
	throw new Error('error')
})

promise.then(val => {
	console.log('success')
}).catch(err => {
	console.log('rejected: ' + err)
})

// rejected Error: error

// 等同于
promise.then(val => {
	console.log('success')
}).then(null, err => {
	console.log('rejected: ' + err)
})

// 等同于

Promise 内部错误不会影响到 Promise 外部代码,通俗说 Promise 会吃掉错误。

const someAsyncThing = function() {
	return new Promise(function(resolve, reject) {
    	// x 未定义会报错
        resolve(x + 2)
    })
}
someAsyncThing().then(function() {
	console.log('everything is great')
}).catch(err => {
    console.log(err)
})

setTimeout(() => {console.log(123)}, 2000

// ReferenceError: x is not defined
// 123

-------------------------------------------

// 换个执行顺序就会有不同结果
someAsyncThing().catch(err => {
    console.log(err)
}).then(function() {
	console.log('everything is great')
})

setTimeout(() => {console.log(123)}, 2000)

// ReferenceError: x is not defined
// everything is great
// 123
  • Prmoise.prototype.finally()
// finally 用于不管promise最后状态如何都会执行的操作
promise
.then(result => {})
.catch(error => {})
.finally(() => {})

Promise 构造函数上的方法

  • Promise.all()
const promise = Promise.all([p1, p2, p3])

promise 的状态由 p1 p2 p3 决定

1)只有 p1 p2 p3 都变成 fulfilled,p的状态才会变为fulfilled。此时 p1 p2 p3 的返回值组成一个数组,传递给p的回调。

2)只要 p1 p2 p3 的一个被 rejected,p的状态就会变为rejected,此时第一个被 reject的实例的返回值,会传递给 p 的回调函数。

  • Promise.race()
const promise = Promise.race([p1, p2, p3])

上面只要 p1 p2 p3 之中有一个状态率先发生改变,p的状态就跟着改变。那个率先改变的 Promise 实例的返回值,就传递给 p 的回调函数。

  • Promise.allSettled()
const promise = [	fetch('/api1'),    fetch('/api2'),    fetch('/api3')]

await Promise.allSettled(promise)
removeLoadingIndicator()

上面代码内部 p1 p2 p3 是三个异步请求,只有三个请求都结束,不管成功还是失败,才会调用下面的方法移除 loading。

  • Promise.any()

接收一组 Promise 实例作为参数,只要有一个变为fulfilled,包装的实例就会变成 fulfilled,如果所有的参数实例变为rejected,包装实例就会变成rejected

  • Promise.resolve()
const promise = Promise.resolve($.ajax('/a.json'))

将现有对象转换为 Promise 对象

Promise.resolve('foo')

new Promise(resolve => resolve('foo'))
  • Promise.reject()
const promise = Promise.reject('error')

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

对于Promise的具体实现方式,我们后在后面开一个手写各类的系列中写一个手写Promise。