Menu

ES6常用方法总结—Promise

1.Promise用法

Promisethen方法会返回一个新的Promise

let p = function(){
    return new Promise((resolve, reject) => {
        try {
            setTimeout(()=>{
               return resolve()
            },500)
        } catch (error) {
            return reject(error)
        }
    })
}

async function do(){
    let res = await p()
    console.log(res)
}

p().then((res)=>{
    console.log(res)
},(err)=>{
    console.log(err)
})

p().then((res)=>{
    console.log(res)
}).catch((err)=>{
    console.log(err)
})

p().then((res)=>{
    console.log(res)
    return p()
}).then((res)=>{
    console.log(res)
}).catch((err)=>{
    console.log(err)
})

2.Promise.all用法(ES11)

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

另外Promise.allSettled看起来像是对Promise.all的一种补充,缓解了使用Promise.all碰到reject的痛点问题。

一句话概括Promise.allSettledPromise.all的最大不同:Promise.allSettled会执行所有的Promise。而Promise.all只要遇到第一个reject的结果,整体就会reject

Promise.allSettled执行后返回的是对象数组,每个对象中包含了各Promise的执行状态(fulfilledrejected)和结果值。

Promise.allSettled()Promise.all()用来做批量异步处理。如果说每一个异步都需要得到结果,就用allSettled()。如果说每一个异步都需要成功,才能往下进行,就用all ()。

let p1 = new Promise((resolve, reject) => {
  resolve('成功了')
})

let p2 = new Promise((resolve, reject) => {
  resolve('success')
})

let p3 = Promse.reject('失败')

Promise.all([p1, p2]).then((result) => {
  console.log(result)               //['成功了', 'success']
}).catch((error) => {
  console.log(error)
})

Promise.all([p1,p3,p2]).then((result) => {
  console.log(result)
}).catch((error) => {
  console.log(error)      // 失败了,打出 '失败'
})

//---------------------

async function fn6(){
	let [a,b,c] = await Promise.all([
		await '成功了a',
		new Error('失败了b'), 
		await '成功了c',
	])
	return [a,b,c]
}
fn6().then(res=>{
	console.log('fn6-res',res)   //fn6-res [ '成功了a', Error: 失败了b, '成功了c' ]
})

//------------------------------

async function foo(){
  return Promise.allSettled([
    await new Promise((resolve) => setTimeout(() => resolve("1"), 2000)),
    await new Promise((resolve, reject) => setTimeout(() => resolve("2")))
  ]);
}

foo().then((data) => {
    console.log(data);  //[{status: 'fulfilled', value: '1'},{status: 'fulfilled', value: '2'}]
})

3.promise原理

promise的核心原理其实就是发布订阅模式,通过两个队列来缓存成功的回调(onResolve)和失败的回调(onReject)。实则也是通过回调函数方式实现异步。

4.promise特点

  1. Promise状态不受外部影响。Promise有三个状态:pending进行中、fulfilled已成功、rejected已失败。只有异步操作的结果才可以决定当前是哪个状态,其他任何操作都无法改变状态。(这也就是promise许诺的由来)
  2. 一旦状态改变,就不会再变。Promise对象状态改变只有两种可能:从pending改到fulfilled或者从pending改到rejected,只要这两种情况发生,状态就凝固了不会再变。这时候就称为定型resolved

5.原生实现Promise(手写Promise)

使用发布订阅模式+状态切换原理实现。由于原型方法诸如then、all

function MyPromise(execution) {
	// 静态属性(常量)
	MyPromise.PENDING = 'pending'
	MyPromise.FULFILLED = 'fulfilled'
	MyPromise.REJECTED = 'rejected'

	this.status = MyPromise.PENDING // 状态
	this.reason = null // 失败原因
	this.value = null // 成功返回值
	// 收集依赖缓存(函数)
	this.resolveDeps = []
	this.rejectDeps = []
	// 以上的遍历也可设为私有变量,再添加get/set方法,这里为简化没做

	// this缓存
	var self = this

	// 成功回调
	function resolve(res) {
		// 判断状态
		if (self.status === MyPromise.PENDING) {
			self.value = res
			self.status = MyPromise.FULFILLED

			// 这种方式可以遍历数组且执行后可以清空数组,简化清空数组的步骤
			while (self.resolveDeps.length) {
				// 发布给所有订阅
				self.resolveDeps.shift()(res)
			}
		}
	}

	// 失败回调
	function reject(err) {
		// 判断状态
		if (self.status === MyPromise.PENDING) {
			self.reason = err
			self.status = MyPromise.REJECTED
			// 发布给订阅
			while (self.rejectDeps.length) {
				self.rejectDeps.shift()(err)
			}
		}
	}


	// 判断参数传入是否是函数
	if (typeof execution === 'function') {

		// 捕获错误
		try {
			// 执行传入的执行器
			execution(resolve, reject)

		} catch (err) {
			reject(err)
		}

	} else {
		throw 'MyPromise 的参数必须为函数。'
	}
}


MyPromise.prototype.then = function(onResolved, onRejected) {
	if ((onRejected && typeof onRejected !== 'function') || typeof onResolved !== 'function') {
		throw 'then 方法的参数必须为函数。'
	}

	// then方法链式 返回新promise
	return new MyPromise((resolve, reject) => {

		const resolvedCb = (val) => {
			let nextVal = onResolved(val);

			if (nextVal instanceof MyPromise) { // promise对象

				nextVal.then(resolve, reject)

			} else { // 普通值

				resolve(nextVal);

			}
		}

		const rejectedCb = (reason) => {
			let nextVal = onRejected(reason);

			if (nextVal instanceof MyPromise) { // promise对象

				nextVal.then(resolve, reject)

			} else { // 普通值

				resolve(nextVal)

			}
		}

		// 执行器是异步操作时,status还是pending,
		// 所以此时并不知道是成功还是失败回调,那么这里就需要把这两个回调
		// 存储起来
		this.resolveDeps.push(resolvedCb)
		this.rejectDeps.push(rejectedCb)
	})
}

MyPromise.prototype.catch = function(onRejected) {
	if (typeof onRejected === 'function') {
		// 收集依赖
		this.addRejectDeps(onRejected)
	} else {
		throw 'catch 方法的参数必须为函数。'
	}
}

MyPromise.prototype.finally = function() {}

MyPromise.prototype.all = function() {}

MyPromise.prototype.allSettled = function() {}

// 测试
var demo = function() {
	return new MyPromise((resolve, reject) => {
		setTimeout(() => {
			resolve(123435)
		}, 1000)
	})
}
demo().then((res) => {
	console.log('=========res', res)
	return new MyPromise((resolve, reject) => {
		setTimeout(() => {
			resolve('erersadad')
		}, 1000)
	})
}).then((res) => {
	console.log('=========res2', res)
})

6.promise和async/await、generator区别

Async/await 是Javascript编写异步程序的新方法。以往的异步方法无外乎回调函数和Promise。但是Async/await建立于Promise之上。

async/await:

  1. async/await 是写异步代码的新方式,以前的方法有回调函数和Promise。
  2. async/await 是基于Promise实现的,它不能用于普通的回调函数。
  3. 在主体函数之前使用了async关键字,在函数体内,使用了await关键字。
  4. wait关键字只能出现在用async声明的函数体内。
  5. 当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。
  6. async/await 与Promise一样,是非阻塞的。
  7. async/await 使得异步代码看起来像同步代码。

promise和async的区别:

  1. async/await 更加语义化,更简洁
  2. async/await可以使用try{}catch(){}捕获错误
  3. 深层嵌套可以使用promise.then方法进行链式请求,也可以使用promise.all方法。但最简洁的方式是使用async
  4. async 的优越性就是把每次异步返回的结果从 then 中拿到最外层的方法中,不需要链式调用

async 和 Generator区别:

  1. async是generator函数的语法糖。
  2. generator 函数是将函数分步骤阻塞 ,只有主动调用 next() 才能进行下一步
var gen1 = function* () {
    var f1 = yield 1;
    var f2 = yield 2;
    console.log(f1);
    console.log(f2);
};

var aaa = gen1() //undefined

aaa.next() //{value: 1, done: false} //执行f1

aaa.next() //{value: 2, done: false} //执行f2

aaa.next() 
//undefined
//undefined
//{value: undefined, done: true} 最终执行完毕

async对Generator函数做了以下4点改变:

  1. 内置执行器: async函数自带执行器,简单的说async函数就相当于自执行的Generator函数
  2. 更好的语义: async表示函数里有异步操作,await表示紧跟再后面的表达式需要等待结果
  3. 更广的适用性: yield命令后,只能是Thunk函数或Promise对象,而async函数的await命令后面,可以是Promise对象和原始类型的值
  4. 返回值是Promise对象,可以使用then方法指定下一步操作

7.Promise如何转async/await(异步转同步)

new Promiose()async

async function getdata (params) {
  return 'hllo Nodejs'
}
//等价于
function getdata () {
   return Promise.resolve('hllo Nodejs')
}

// async 返回 reject
async function getdata2 (params) {
  return Promise.reject(1234)
}
// 或者
async function aaa (){
  throw 1234
}
// 使用then执行async
getdata2().catch(a=>console.log(a))  //1234

Promiose.then()await

async function fooAsync () {
  const data = await 1
  console.log(data)
}
//等价于
function fooAsync () {
  return Promise.resolve(1).then((data) => console.log(data))
}

await 后面可以是Promise或任意表达式,async函数返回的也是Promise

async function foo1() {
    const result1 = await new Promise((resolve) =>  resolve('1'))
    const result2 = await new Promise((resolve) => resolve('2'))
    return result1 + result2
}

async function foo2() {
    const result = await foo1()
}

async返回一个数组

内部是使用Promise.all()方法来执行,返回可迭代的数组,全部成功才返回成功,一个失败所有的都失败。

async function foo() {
    const result1 = await new Promise((resolve) => {
        setTimeout(() => resolve({ name: "selfsummer" }), 2000);
    });
    const result2 = await new Promise((resolve, reject) => setTimeout(() => resolve({ name: "自夏" })));
    return [result1, result2];
}

foo().then((data) => {
    Array.isArray(data)    // true
    console.log(data)     //[ { name: 'selfsummer' }, { name: '自夏' } ]
}).catch((err) => {
    console.log(err);
});

8.promise和setTimeout区别

Promise本身是同步的立即执行函数, 当在executor中执行resolve或者reject的时候, 此时是异步操作, 会先执行then/catch等,当主栈完成后,才会去调用resolve/reject中存放的方法执行

setTimeout不会阻碍后续代码执行,会放到任务队列中。可以理解为一个异步函数。但论其原理实则不是严格意义上的异步,而只是推迟处理。

Promise中的异步操作(promise.then)是微任务。而setTimeout属于宏任务。微任务要优先于宏任务执行。

js事件循环机制总结

9.promise是异步还是同步

promise本身是同步的,其then方法是异步的(promise.then属于异步微任务,then中的方法,必须等到所有的同步任务执行完才执行)。promise是无法取消的,一旦新建就会立即执行。如果不设置回调函数,promise内部的错误是无法反应到外部的。当处于“pending”状态时,无法得知目前进展到哪个阶段(刚刚开始还是即将完成)。

异步的三种实现方式

  1. 回调函数(回调函数不一定是异步 , 但异步一定有回调函数)
  2. 事件
  3. promise 对象

10.Promise在resolve和reject执行之后,后续代码还会执行吗

会的。因为resolve和reject本身是回调函数,无法中断后续代码。需要使用return去中断。所以一般使用默认加return较妥。

function p() {
      return new Promise((resolve, reject) => {
        console.log(222)
        setTimeout(() => {
          console.log('async in p')
          reject('err in p')
          console.log('content after p reject')
        }, 200)
      })
}
function p1() {
      return new Promise((resolve, reject) => {
        console.log(333)
        setTimeout(() => {
          console.log('async in p1')
          resolve('resolev in p1')
          console.log('content after p1 resolve')
        }, 250)
      })
}
function p2() {
      return new Promise((resoleve, reject) => {
        console.log(444)
        setTimeout(() => {
          console.log('async in p2')
          return reject(new Error('p2error'))
          console.log('content after p2 return')
        }, 300)
      })
}

10.Promise的resolve和reject能否用return和throw代替

return无法取代resolve,因为return无法改变 <pending> 状态为<fulfilled>,在底层,需要执行resolve回调函数,才能改变状态。

throw可以取代reject,因为在底层,会有try/catch的错误捕获,如若出错,会将状态置为<rejected>

当然,在async函数中可以使用return和throw实现。

本文固定连接:https://code.zuifengyun.com/2019/05/3350.html,转载须征得作者授权。

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!

¥ 打赏支持