Promise相关知识点由浅入深包含以下这些。掌握这些,能让你基本功更扎实,写代码更得心应手,面试更无往不利。
一、Promise封装ajax
function ajax(url = '', method = 'GET', data = {}) { return new Promise((resolve, reject) => { let options = { method } if(method === 'GET') { url += '?' + Object.entries(data).map(arr => arr[0] + '=' + arr[1]).join('&') } else if(method === 'POST') { options.body = JSON.stringify(data) options.headers = { 'Content-Type': 'application/json' } } fetch(url, options).then(res => res.json()) .then(data => resolve(data)) .catch(e => reject(e)) }) } //使用范例 ajax('http://api2.jirengu.com/getWeather.php', 'GET', {city: '杭州'}) .then(data => console.log(data)) .catch(e => console.log(e)) ajax('https://note-server.hunger-valley.com/auth/login', 'POST', {username: 'jirengu', password: '123456'}) .then(data => console.log(data)) .catch(e => console.log(e))
Promise.all
Promise.all() 方法接收一个数组做为输入严格来说是iterable类型,Array,Map,Set都属于iterable类型),数组的每一项是Promise实例(如果不是会创建一个resolve的Promise实例),运行结果返回一个Promise实例。 当数组里promise全部 resolve时再resolve,结果是所有promise resolve的结果构成的一个数组。 如果中途任何一个promise 执行reject,则Promise.all得到的Promise实例立即reject,
let p1 = new Promise(resolve => setTimeout(resolve, 2000, 1)) let p2 = new Promise(resolve => setTimeout(resolve, 3000, 2)) let p3 = new Promise((resolve, reject) => { let v = Math.random() if(v > 0.5) setTimeout(resolve, 2000, v) else setTimeout(reject, 2000, v) }) Promise.all([p1, p2, p3]) .then(value => console.log(value)) //[1, 2, 0.7685334] .catch(reason => console.error(reason)) //或者 0.2342321 console.log(p1, p2, p3) //如果p1 p2 p3都resolve,则当前promise实例 resolve,va是p1 p2 p3 resolve的结果构成的数组 //如果p1 p2 p3 任一个reject,则当前promise实例立即reject,reason是p1 p2 p3中最先reject的值
Promise.allSettled
该Promise.allSettled()方法返回一个在所有给定的promise都已经fulfilled或rejected后的promise,并带有一个对象数组,每个对象表示对应的promise结果。
let p1 = new Promise((resolve, reject) => setTimeout(resolve, 2000, 1)) let p2 = new Promise((resolve, reject) => setTimeout(resolve, 3000, 2)) let p3 = new Promise((resolve, reject) => setTimeout(reject, 2000, 3)) Promise.allSettled([p1, p2, p3]) .then(value => console.log(value)) /* 输出结果 [ {"status":"fulfilled","value":1}, {"status":"fulfilled","value":2}, {"status":"rejected","reason":3} ] */
总结一下,Promise.all 会拿到所有promise对象resolve的结果,但如果中间任意一个promise提前reject则立即退出进入失败逻辑;Promise.allSettled会等所有promise全部有结果(不管成功还是失败)后拿到所有结果构成的数组。
Promise.race
Promise.race(iterable)方法返回一个 promise,一旦迭代器中的某个promise resolve或reject,返回的 promise就会resolve或reject。
let p1 = new Promise((resolve, reject) => setTimeout(resolve, Math.random()*1000, 1)) let p2 = new Promise((resolve, reject) => setTimeout(resolve, Math.random()*1000, 2)) let p3 = new Promise((resolve, reject) => setTimeout(reject, Math.random()*1000, 3)) Promise.race([p1, p2, p3]) .then(value => console.log(value)) //p1 p2 p3谁最先resolve,就进入这里 .catch(reason => console.error(reason)) ////p1 p2 p3谁最先reject,就进入这里
手写Promise.all
//参数:一个迭代器对象,比如 String, Array, TypedArray, Map, and Set //如果传递了一个空迭代器,返回的Promise对象会同步的resolve,resolve的值是空对象 //如果传递了一个非空迭代器,并且所有的promise都是fulfill状态的,或者不是promise对象,那么返回的Promise对象会异步的resolve //如果任一个传递的promise对象被reject,Promise.all会异步的reject,reject的值是那个传递的promise对象reject的值 Promise.all = function(iterable) { return new Promise((resolve, reject) => { let promises = [...iterable].map(p => (p instanceof Promise) ? p : Promise.resolve(p)) if(promises.length === 0) return resolve([]) let values = [] let count = 0 for(let i=0; i < promises.length; i++) { promises[i].then(v => { values[i] = v count++ if(count === promises.length) { resolve(values) } }, reject) } }) }
手写Promise.race
//参数,一个iterable对象。比如 String, Array, TypedArray, Map, and Set //race 函数返回一个 Promise,它将与iterable对象里最先resolve或者reject的 promise 相同的完成方式被处理。 //如果传的迭代是空的,则返回的 promise 一直处于 pending。 //如果迭代包含一个或多个非Promise的值,或者包含已经resolve或者reject的Promise对象,则 Promise.race 把第一个这种值拿作为被处理的对象。 Promise.race = function(iterable) { return new Promise((resolve, reject) => { let promises = [...iterable].map(p => (p instanceof Promise) ? p : Promise.resolve(p)) for(let i=0; i<promises.length; i++) { promises[i].then(resolve, reject) } }) } //测试 //eg1: 迭代器包含非Promise值和已resolve的Promise对象,以第一个为准 Promise.race([3, Promise.resolve(4), 5]) .then(v => console.log(v)) .catch(reason=> console.error(reason)) //eg2: 迭代为空,返回pending状态的promise console.log(Promise.race('')) let p1 = new Promise(r => setTimeout(r, 100, 1)) let p2 = new Promise(r => setTimeout(r, 200, 2)) let p3 = new Promise((r, j) => setTimeout(j, 50, 3)) //eg3: 迭代器里包含已经resolve的promise和pending状态的promise,以已resolve的promise对象为准 Promise.race([Promise.reject(4), p1, p2]) .then(v => console.log(v)) .catch(reason=> console.error(reason)) //eg3: 迭代器里包含pending状态的promise,以最先resolve或者reject的额的为准 Promise.race([p1, p2]) .then(v => console.log(v)) .catch(reason=> console.log(reason)) Promise.race([p1, p3]) .then(v => console.log(v)) .catch(reason=> console.error(reason))
手写Promise.allSettled
//参数:一个iterable对象。比如 String, Array, TypedArray, Map, and Set //返回一个Promise对象,会异步resolve,结果是一个数组,里面每一项是个对象,包含 status 和 value 或者reason两个属性。每个对象是iterable对象每一个 resolve或者reject的结果构成的对象 //如果iterable对象为空,则返回一个已经被resolve的Promise对象,resolve内容为空数组 Promise.allSettled = function(iterable) { return new Promise((resolve, reject) => { let promises = [...iterable].map(p => (p instanceof Promise) ? p : Promise.resolve(p)) if(promises.length === 0) return resolve([]) let values = [] let count = 0 for(let i=0; i<promises.length; i++) { promises[i].then(v => { values[i] = { status: 'fulfilled', value: v } }, reason => { values[i] = { status: 'rejected', reason: reason } }).finally(() => { count++ if(count === promises.length) { resolve(values) } }) } }) } //测试 Promise.allSettled([3, Promise.resolve(4), 5]) .then(v => console.log(v)) console.log(Promise.allSettled('')) let p1 = new Promise(r => setTimeout(r, 100, 1)) let p2 = new Promise(r => setTimeout(r, 200, 2)) Promise.allSettled([Promise.reject(4), p1, p2]) .then(v => console.log(v))
手写Promise.any
//参数:一个iterable对象。比如 String, Array, TypedArray, Map, and Set //返回一个Promise对象, //如果iterable为空,则返回一个已经rejected的Promise //如果iterablee传递的不包含promise对象,则返回一个异步resolved的Promise //其他情况,返回一个pending状态的Promise。当任何一个参数promise对象resolve时,这个Promise对象异步resolve。当所有参数promise对象都reject时,这个Promise对象异步reject Promise.any = function(iterable) { return new Promise((resolve, reject) => { let promises = [...iterable].map(p => (p instanceof Promise) ? p : Promise.resolve(p)) if(promises.length === 0) return reject('AggregateError: All promises were rejected') let rejectCount = 0 for(let i=0; i<promises.length; i++) { promises[i].then(resolve, reason => { rejectCount++ if(rejectCount === promises.length) { reject('AggregateError: All promises were rejected') } }) } }) } //测试 Promise.any([3, Promise.reject(4), 5]) .then(v => console.log(v)) .catch(reason=> console.error(reason)) console.log(Promise.any2('').catch(e=>console.error(e))) let p1 = new Promise(r => setTimeout(r, 100, 1)) let p2 = new Promise(r => setTimeout(r, 200, 2)) Promise.any([Promise.reject(4), p1, p2]) .then(v => console.log(v)) .catch(reason=> console.error(reason)) Promise.any([p1, p2]) .then(v => console.log(v)) .catch(reason=> console.log(reason)) let p3 = new Promise((r, j) => setTimeout(j, 50, 3)) Promise.any([p3]) .then(v => console.log(v)) .catch(reason=> console.error(reason))
手写Promise.last
//Promise.first 等同于Promise.any //反问: Promise.last怎么实现? Promise.last = function(iterable) { return new Promise((resolve, reject) => { let promises = [...iterable].map(p => (p instanceof Promise) ? p : Promise.resolve(p)) if(promises.length === 0) return reject('AggregateError: All promises were rejected') let resolveCount = 0 let rejectCount = 0 let lastValue = null for(let i=0; i<promises.length; i++) { promises[i].then(v => { resolveCount++ lastValue = v }, reason => { rejectCount++ }).finally(() => { if(rejectCount === promises.length) { reject('AggregateError: All promises were rejected') } else if(resolveCount + rejectCount === promises.length) { resolve(lastValue) } }) } }) } let p1 = new Promise(r => setTimeout(r, 100, 1)) let p2 = new Promise(r => setTimeout(r, 200, 2)) let p3 = new Promise(r => setTimeout(r, 300, 3)) Promise.last([Promise.reject(4), p1, p2, p3]) .then(v => console.log(v)) .catch(reason=> console.error(reason))
手写Promise.queue
Promise.queue = function(arr, initValue) { return new Promise((resolve, reject) => { let sequence = Promise.resolve(initValue) arr.forEach(item => { sequence = sequence.then(item) }) sequence.then(resolve, reject) }) } const f1 = function(v) { console.log('f1', v) return new Promise((resolve, reject) => setTimeout(resolve, 1000, v+1)) } const f2 = function(v) { console.log('f2', v) return new Promise((resolve, reject) => setTimeout(resolve, 1000, v+1)) } Promise.queue([f1, f2], 10).then(v => console.log(v))
function asyncPool(fn, arr, limit = 10) { let args = [...arr] //不修改原参数数组 let results = [] //存放最终结果 let runningCount = 0 //正在运行的数量 let resultIndex = 0 //结果的下标,用于控制结果的顺序 let resultCount = 0 //结果的数量 return new Promise((resolve) => { function run() { while(runningCount < limit && args.length > 0) { runningCount++ ((i)=> { //闭包用于保存结果下标,便于在resolve时把结果放到合适的位置 let v = args.shift() console.log('正在运行' + runningCount) fn(v).then(val => { results[i] = val }, () => { throw new Error(`An error occurred: ${v}`) }).finally(() => { runningCount-- resultCount++ if(resultCount === arr.length) { //这里之所以用resultCount做判断,而不用results的长度和args的长度,是因为这两个都不准确 resolve(results) } else { run() } }) })(resultIndex++) } } run() }) } // 测试 function getWeather(city) { console.log(`开始获取${city}的天气`) return fetch(`https://api2.jirengu.com/getWeather.php?city=${city}`).then(res=> res.json()) } let citys = ['北京', '上海', '杭州', '成都', '武汉', '天津', '深圳', '广州', '合肥', '郑州'] asyncPool(getWeather, citys, 2).then(results => console.log(results))
function promisify(fn, context = null) { return function(...args) { return new Promise((resolve, reject) => { fn.bind(context)(...args, function(err, val) { if(err !== null) reject(err) else resolve(val) }) }) } } //测试 function add(a, b, callback) { setTimeout(() => { if(a + b <= 100) { callback(null, a + b) } else { callback('大于 100') } }, 1000) } add(3, 4, function(err, val) { if(err) { console.error(err) return } console.log(val) }) let add2 = promisify(add) add2(100, 50) .then(v => console.log(v)) .catch(err => console.error(err))
https://zhuanlan.zhihu.com/p/449183802https://zhuanlan.zhihu.com/p/450906325
未完待续
饥人谷一直致力于培养有灵魂的编程者,打造专业有爱的国内前端技术圈子。如造梦师一般帮助近千名不甘寂寞的追梦人把编程梦变为现实,他们以饥人谷为起点,足迹遍布包括facebook、阿里巴巴、百度、网易、京东、今日头条、大众美团、饿了么、ofo在内的国内外大小企业。 了解培训课程:加微信 xiedaimala03,官网:https://jirengu.com