# promise 专题

# 目录

DETAILS
  • 如何用 await 和 async 写一个睡眠函数?
  • 请实现一个 cacheRequest 方法
  • 三次重试:请实现函数 retry,把 job 作为 retry 函数的参数传入
  • 异步最大并发请求并按顺序组成结果
  • 同时处理请求并发数,一次并发一组 n 个请求,有一个执行完成,就能并发下一组 n
  • 串行 Promise 控制,一个请求执行完再执行下一个
  • Promise 每隔一秒打印数字
  • 使用 Promise 实现红绿灯交替重复亮
  • 实现 mergePromise 函数
  • 封装一个异步加载图片的方法
  • 限制异步操作的并发个数并尽可能快的完成全部
  • 实现 finally
  • 实现 Promise.all
  • 根据 promiseA+实现 promise
    • 步骤一:实现成功和失败的回调方法
    • 步骤二:then 方法链式调用
  • Promise 的错误捕获
  • 终值与拒因 终值:指的是 promise 被解决时传递给解决回调的值 拒因:拒绝原因,指在 promise 被拒绝时传递给异常回调的拒绝原因
  • 终值和拒因的穿透(忽略它)特性
    • 如果 promise 的状态变为 fulfilled,then 方法没有注册 onFulfilled
    • 如果 promise 的状态变为 rejected,then 方法没有注册 onRejected
    • 另外,.then 或者 .catch 的参数期望是函数,传入非函数则会发生值透传(忽略该值)
      • 怎么理解值穿透?(https://blog.csdn.net/u013448372/article/details/110863799)
Promise.resolve(1)
	.then(2)
	.then(Promise.resolve(3))
	.then(console.log)
	.then(console.log("lll"));

// 1
// resolve(1)过去,then只接受2个函数,console.log 函数传进去,接收了1,于是打印1

// 这里有个问题,console.log('lll')为什么先于1输出
Promise.resolve(1)
	.then(2)
	.then(Promise.resolve(3))
	.then(console.log)
	.then(console.log("lll"));
  • Promise.resolve
let p = Promise.resolve(x);
// 等价于
let p = new Promise(resolve => {
	resolve(x);
});
  • Promise 的问题 无法取消 Promise,若没有状态变更,也无法停止 promise 的等待 不设定 then 或 catch 方法,构造函数(excutor 函数)错误,无法捕获 未完成状态时,无法得知是刚开始,还是即将完成

  • .all 与 .race

    • Promise.all()的作用是接收一组异步任务,然后并行执行异步任务,并且在所有异步操作执行完后才执行回调。
    • .race()的作用也是接收一组异步任务,然后并行执行异步任务,只保留取第一个执行完成的异步操作的结果,其他的方法仍在执行,不过执行结果会被抛弃。
    • Promise.all().then()结果中数组的顺序和 Promise.all()接收到的数组顺序一致。
    • all 和 race 传入的数组中如果有会抛出异常的异步任务,那么只有最先抛出的错误会被捕获,并且是被 then 的第二个参数或者后面的 catch 捕获;但并不会影响数组中其它的异步任务的执行。
  • async 与 await

    • 「紧跟着 await 后面的语句相当于放到了 new Promise 中,下一行及之后的语句相当于放在 Promise.then 中,await 会阻塞下一行及之后代码的执行」
    • async 处理错误
      • 如果在 async 函数中抛出了错误,则终止错误结果,不会继续向下执行。
      • 想要使得错误的地方不影响 async 函数后续的执行的话,可以使用 try catch

# details

例题
  • 如何用 await 和 async 写一个睡眠函数?
function sleep(ms) {
	return new Promise(resolve => {
		setTimeout(() => {
			resolve("sleep for " + ms + " ms");
		}, ms);
	});
}

async function run(time) {
	let result = await sleep(time);
	console.log(result);
}

run(3000);
  • 请实现一个 cacheRequest 方法,保证当使用 ajax(请求相同资源时,此题中相同资源的判断是以 url 为判断依据),真实网络层中,实际只发出一次请求(假设已存在 request 方法用于封装 ajax 请求,调用格式为:request(url, successCallback, failCallback)) 比如调用方代码(并行请求)如下
cacheRequest("/user", data => {
	console.log("我是从A中请求的user,数据为" + data);
});

cacheRequest("/user", data => {
	console.log("我是从B中请求的user,数据为" + data);
});
function request(url, successCallback, failCallback) {
    return fetch(url).then(successCallback).catch(failCallback)
}

function cacheRequest(url, successCallback, failCallback) {
    cacheRequest.cache = cacheRequest.cache || {};
    cacheRequest.clear = cacheRequest.clear || () => (cacheRequest.cache = undefined);

    if (cacheRequest.cache[url]) {
        return cacheRequest.cache[url].then(successCallback).catch(failCallback);
    }

    // 缓存请求
    let success, fail;
    cacheRequest.cache[url] = new Promise((resolve, reject) => {
        success = resolve;
        fail = reject;
    });
    // return fetch(url)
    //     .then(response => {
    //         success(response.clone())
    //         successCallback(response)
    //     })
    //     .catch(error => {
    //         failCallback(error)
    //         fail(error)
    //     })
    return request(
        url,
        response => {
            success(response.clone());
            successCallback(response);
        },
        error => {
            failCallback(error);
            fail(error);
        }
    );
}

  • 三次重试:假设有一个函数名为 job,调用 job 后会执行一些异步任务,并返回一个 Promise,但 job 执行的异步任务有可能会失败 请实现函数 retry,把 job 作为 retry 函数的参数传入,当 retry 执行后会尝试调用 job,如果 job 返回成功(即 Promise fulfilled),则 retry 函数返回 job 函数的返回内容; 如果 job 返回失败(即 Promise rejected),retry 函数会再次尝试调用 job 函数。 如果 job 连续三次均返回失败,retry 则不再尝试调用,并返回其最后一次失败的内容。
// 方法1. 可以在attempt中每次new一个新的Promise对象
let count = 0;
function job() {
	return new Promise((resolve, reject) => {
		count++;
		if (count === 6) {
			resolve("成功");
		} else {
			reject("失败");
		}
	});
}
function retry(job, times, delay) {
	let flag = 1;
	const attempt = () => {
		new Promise((resolve, reject) => {
			job()
				.then(response => {
					console.log(`${flag}次成功`);
					resolve(response);
				})
				.catch(err => {
					console.log(`重试第${flag}`);
					if (flag === times) {
						reject(err);
					} else {
						flag++;
						setTimeout(() => {
							resolve(attempt());
						}, delay);
					}
				});
		}).catch(err => {
			console.log(`重试${flag}次失败`, err);
		});
	};
	attempt();
}

// retry(job, 3, 1000);
retry(job, 7, 100);

// 方法2. 也可以将attempt放在一个Promise之中

let count = 0;
function job() {
	return new Promise((resolve, reject) => {
		count++;
		if (count === 6) {
			resolve("成功");
		} else {
			reject("失败");
		}
	});
}

function retry(job, times, delay) {
	let flag = 1;
	new Promise((resolve, reject) => {
		var attempt = function() {
			job()
				.then(response => {
					console.log(`${flag}次成功`);
					resolve(response);
				})
				.catch(err => {
					console.log(`重试第${flag}`);
					if (flag == times) {
						reject(err);
					} else {
						flag++;
						setTimeout(() => {
							attempt();
						}, delay);
					}
				});
		};
		attempt();
	}).catch(err => {
		console.log(`重试${flag}次失败`, err);
	});
}

retry(job, 3, 1000);
  • 异步最大并发请求并按顺序组成结果 虽然 map 方法的参数是 async 函数,但它是并发执行的,因为只有 async 函数内部是继发执行,外部不受影响。后面的 for..of 循环内部使用了 await,因此实现了按顺序输出。
async function asyncInOrder(urls) {
	// 并发读取远程URL
	const promises = urls.map(async url => {
		const res = await fetch(url);
		return res.status; // 比如把状态码返回
	});

	// 按次序输出
	for (const p of promises) {
		console.log(await p);
	}
}
asyncInOrder(["xxx", "yyyy"]);
  • 同时处理请求并发数,一次并发一组 n 个请求,有一个执行完成,就能并发下一组 n(这里用 race 的话,返回一个剩下的结果就被抛弃了)
let p1 = () => {
	return new Promise(resolve => {
		console.log("do p1");
		setTimeout(() => {
			resolve("success p1 2s");
		}, 2000);
	});
};

let p2 = () => {
	return new Promise(resolve => {
		console.log("do p2");
		setTimeout(() => {
			resolve("success p2 2.5s");
		}, 2500);
	});
};

let p3 = () =>
	new Promise((resolve, reject) => {
		console.log("do p3");
		setTimeout(() => {
			reject("error p3 3s");
		}, 3000);
	});
let p4 = () => {
	return new Promise(resolve => {
		console.log("do p4");
		setTimeout(() => {
			resolve("success p4 1s");
		}, 1000);
	});
};

let p5 = () => {
	return new Promise(resolve => {
		console.log("do p5");
		setTimeout(() => {
			resolve("success p5 2s");
		}, 2000);
	});
};

let p6 = () =>
	new Promise((resolve, reject) => {
		console.log("do p6");
		setTimeout(() => {
			reject("error p6 3s");
		}, 3000);
	});
let p7 = () => {
	return new Promise(resolve => {
		console.log("do p7");
		setTimeout(() => {
			resolve("success p7 3s");
		}, 3000);
	});
};

let p8 = () => {
	return new Promise(resolve => {
		console.log("do p8");
		setTimeout(() => {
			resolve("success p8 3s");
		}, 3000);
	});
};

let p9 = () =>
	new Promise((resolve, reject) => {
		console.log("do p9");
		setTimeout(() => {
			reject("error p9 2.5s");
		}, 2500);
	});

let p10 = () =>
	new Promise((resolve, reject) => {
		console.log("do p10");
		setTimeout(() => {
			reject("error p10 4s");
		}, 4000);
	});

// 并发promise控制,分组然后递归
function promiseConcurrent(pArr, n) {
	const iteratorPromise = arr => {
		const iter = () => {
			if (arr.length) {
				let sub = arr.shift();
				Promise.race(sub.map(item => item()))
					.then(res => {
						console.log(res);
						iter();
					})
					.catch(err => {
						console.log(err);
					});
			}
		};

		iter();
	};

	let pArrList = [];
	if (n > pArr.length) {
		Promise.all(pArr.map(p => p()))
			.then(response => {
				console.log(response);
			})
			.catch(err => {
				console.log(err);
			});
	} else {
		while (pArr.length > n) {
			let sub = pArr.splice(0, n);
			pArrList = pArrList.concat([sub]);
		}

		pArrList = pArrList.concat([pArr]);

		iteratorPromise(pArrList);
	}
}

// promiseConcurrent([p1, p2, p3, p4, p5, p6, p7, p8, p9, p10], 3);
// promiseConcurrent([p1, p2, p3, p4, p5, p6, p7, p8, p9, p10], 15);

// 因为promise.race一个p返回后其他的结果都拿不到了,所以这里提供一种思路,把所有请求实例都收集起来
// 不过这样只能在执行完成后才能拿到全部结果,因此应该采用race替换index的思路来做,即“限制异步操作的并发个数并尽可能快的完成全部”
const myCollector = collector();

promiseConcurrent(
	[p1, p2, p3, p4, p5, p6, p7, p8, p9, p10].map(item =>
		myCollector.collect(item)
	),
	3
);

setTimeout(() => {
	console.log(myCollector.results);
	console.log(myCollector.errors);
}, 6000); // 不过这样只能在执行完成后才能拿到全部结果,

function collector() {
	const results = new Proxy([], {
		set(...args) {
			return Reflect.set(...args);
		}
	});
	const errors = new Proxy([], {
		set(...args) {
			return Reflect.set(...args);
		}
	});

	return {
		results,
		errors,
		collect: asyncFn => () =>
			asyncFn()
				.then(results.push.bind(results))
				.catch(errors.push.bind(errors))
	};
}
  • 串行 Promise 控制,一个请求执行完再执行下一个
let p1 = () => {
    return new Promise(resolve => {
        setTimeout(() => {
            console.log('p1', Date.now())
            resolve('success p1')
        }, 1000)
    })
}

let p2 = () => {
    return new Promise(resolve => {
        setTimeout(() => {
            console.log('p2', Date.now())
            resolve('success p2')
        }, 2000)
    })
}

let p3 = () => new Promise((resolve, reject) => {
    setTimeout(() => {
        console.log('p3', Date.now())
        reject('error p3')
    }, 3000)
})

let p4 = () => new Promise(resolve => {
    setTimeout(() => {
        console.log('p4', Date.now())
        resolve('success p4')
    }, 4000)
})1)通过在 then 方法里面递归传递下一次异步方法(递归的方法catch后就不会再递归调用迭代了,即p3报错后p4不再执行)
function iteratorPromise1(pArr) {
    const iter = () => {
        if (pArr.length) {
            let p = pArr.shift()
            p().then(res => {
                console.log(res)
                iter()
            }).catch(e => {
                console.log(e)
            })
        }
    }

    iter()
}2)利用 Promise.resolve(),循环赋值(循环调用的方法会执行每一个p,当p3报错后,p4也会执行不过是直接拿到穿透的p3报错)
function iteratorPromise2(pArr) {
    let resolve = Promise.resolve()
    pArr.forEach(p => {
        resolve = resolve.then(() => p())
            .catch(e => {
                console.log(e)
            })
    })
}

iteratorPromise1([p1, p2, p3, p3])
iteratorPromise2([p1, p2, p3, p3])
  • Promise 每隔一秒打印数字
    • 也是串行输出,只是需要结合 setTimeout
function delayPromise(arr) {
	let resolve = Promise.resolve();
	arr.forEach(x => {
		resolve = resolve.then(
			() =>
				new Promise(resolve => setTimeout(() => resolve(console.log(x)), 1000))
		);
	});
}

// 同理可以用Promise配合着reduce不停的在promise后面叠加.then
function delayPromise(arr) {
	arr.reduce((resolve, x) => {
		return resolve.then(
			() =>
				new Promise(resolve => setTimeout(() => resolve(console.log(x)), 1000))
		);
	}, Promise.resolve());
}

delayPromise([1, 2, 3]);
  • 使用 Promise 实现红绿灯交替重复亮(依然考察 Promise.resolve().then(() => return new Promise())的串行输出)
function red() {
	console.log("red");
}
function green() {
	console.log("green");
}
function yellow() {
	console.log("yellow");
}

function light(timer, cb) {
	return new Promise(resolve => {
		setTimeout(() => {
			cb();
			resolve();
		}, timer);
	});
}

function run() {
	Promise.resolve()
		.then(() => {
			return light(3000, red);
		})
		.then(() => {
			return light(2000, green);
		})
		.then(() => {
			return light(1000, yellow);
		})
		.then(() => {
			return run();
		});
}

run();
  • 实现 mergePromise 函数
    • 实现 mergePromise 函数,把传进去的数组按顺序先后执行,并且把返回的数据先后放到数组 data 中。
    • 有点类似于 Promise.all(),不过.all()不需要管执行顺序,只需要并发执行就行了。但是这里需要等上一个执行完毕之后才能执行下一个
// 要求分别输出
// 1
// 2
// 3
// done
// [1, 2, 3]

const time = (timer) => {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve()
    }, timer)
  })
}
const ajax1 = () => time(2000).then(() => {
  console.log(1);
  return 1
})
const ajax2 = () => time(1000).then(() => {
  console.log(2);
  return 2
})
const ajax3 = () => time(1000).then(() => {
  console.log(3);
  return 3
})

mergePromise([ajax1, ajax2, ajax3]).then(data => {
  console.log("done");
  console.log(data); // data 为 [1, 2, 3]
});

// 完成mergePromise函数
async function mergePromise(arr) {
    let data = []
    let promises = arr.map(async (req) => {
        return await req()
    })

    for(let promise of promises) {
        let res = await promise
        data.push(res)
    }

    return data // async 直接返回data
}

// 2
// 3
// 1
// done
// [1, 2, 3]

显然,这里使用map异步并发请求是错误的,虽然最后的data是按顺序组成的,但因为map是并发的,所以会按照定时器执行输出231,而不是题目要求的123

因此,依然需要使用`Promise.resolve()`来实现串行执行:
// 第一次的then为了用来调用ajax
// 第二次的then是为了获取ajax的结果
function mergePromise(arr) {
    const data = []
    let resolve = Promise.resolve()
    arr.forEach(ajax => {
        resolve = resolve.then(ajax).then(res => {
            data.push(res)

            return data // 把每次的结果返回
        })
    })

    return resolve // resolve就拿到了所有data
}

// 或用reduce,注意每次迭代返回data给resolve,最后返回res
function mergePromise(arr) {
    let data = []
    let res = arr.reduce((resolve, ajax) => {
        return resolve.then(ajax).then(res => {
            data.push(res)

            return data
        })
    }, Promise.resolve())

    return res
}
  • 根据 promiseA+实现 promise
    • 步骤一:实现成功和失败的回调方法
    • 步骤二:then 方法链式调用
// 面试版
// 未添加异步处理等其他边界情况
// ①自动执行函数,②三个状态,③then
class Promise {
	constructor(executor) {
		// 三个状态
		this.state = "pending";
		this.value = undefined;
		this.reason = undefined;
		let resolve = value => {
			if (this.state === "pending") {
				this.state = "fulfilled";
				this.value = value;
			}
		};
		let reject = value => {
			if (this.state === "pending") {
				this.state = "rejected";
				this.reason = value;
			}
		};
		// 自动执行函数
		try {
			executor(resolve, reject);
		} catch (e) {
			reject(e);
		}
	}
	// then
	then(onFulfilled, onRejected) {
		switch (this.state) {
			case "fulfilled":
				onFulfilled(this.value);
				break;
			case "rejected":
				onRejected(this.reason);
				break;
			default:
		}
	}
}
class Promise {
	constructor(executor) {
		this.status = "pending";
		this.value = undefined;
		this.reason = undefined;
		// 存放回调
		this.onResolvedCallbacks = [];
		this.onRejectedCallbacks = [];

		let resolve = value => {
			if (this.status === "pending") {
				this.value = value;
				this.status = "resolved";
				this.onResolvedCallbacks.forEach(cb => cb());
			}
		};

		let reject = reason => {
			if (this.status === "pending") {
				this.reason = reason;
				this.status = "rejected";
				this.onRejectedCallbacks.forEach(cb => cb());
			}
		};

		// 如果executor执行报错,直接执行reject
		try {
			executor(resolve, reject);
		} catch (e) {
			reject(e);
		}
	}

	then(onFulfilled, onRejected) {
		let promise2 = new Promise((resolve, reject) => {
			if (this.status === "resolved") {
				let x = onFulfilled(this.value);
				resolvePromise(promise2, x, resolve, reject);
			}
			if (this.status === "rejected") {
				let x = onRejected(this.reason);
				resolvePromise(promise2, x, resolve, reject);
			}
			// 当resolve在setTomeout内执行,then时state还是pending等待状态 我们就需要在then调用的时候,将成功和失败存到各自的数组,一旦reject或者resolve,就调用它们
			if (this.status === "pendding") {
				this.onResolvedCallbacks.push(() => {
					let x = onFulfilled(this.value);
					resolvePromise(promise2, x, resolve, reject);
				});
				this.onRejectedCallbacks.push(() => {
					let x = onRejeced(this.reason);
					resolvePromise(promise2, x, resolve, reject);
				});
			}
		});

		// 返回promise,完成链式
		return promise2;
	}
}

// 完成resolvePromise函数: 让不同的promise代码互相套用
function resolvePromise(promise2, x, resolve, reject) {
	if (x === promise2) {
		return reject(new TypeError("循环引用了少年!!!"));
	}

	let called; // 防止多次调用
	if (x != null && (typeof x === "object" || typeof x === "function")) {
		try {
			// 声明then = x的then方法
			let then = x.then;
			// 如果then是函数,就默认是promise了
			if (typeof then === "function") {
				then.call(
					x,
					y => {
						if (called) return;
						called = true;
						resolvePromise(promise2, x, resolve, reject);
					},
					err => {
						if (called) return;
						called = true;
						reject(err);
					}
				);
			} else {
				resolve(x);
			}
		} catch (e) {
			if (called) true;
			called = true;
			reject(e);
		}
	} else {
		resolve(x);
	}
}
  • 封装一个异步加载图片的方法
function loadImg(url) {
	return new Promise((resolve, reject) => {
		const img = new Image();
		img.onload = function() {
			console.log("一张图片加载完成");
			resolve(img);
		};
		img.onerror = function() {
			reject(new Error("Could not load image at" + url));
		};
		img.src = url;
	});
}
  • 限制异步操作的并发个数并尽可能快的完成全部
    • 以每次并发请求的数量为 3 为例:先请求 urls 中的前面三个(下标为 0,1,2),并且请求的时候使用 Promise.race()来同时请求,三个中有一个先完成了(例如下标为 1 的图片),我们就把这个当前数组中已经完成的那一项(第 1 项)换成还没有请求的那一项(urls 中下标为 3)。
    • 直到 urls 已经遍历完了,然后将最后三个没有完成的请求(也就是状态没有改变的 Promise)用 Promise.all()来加载它们。
function limitLoad(urls, handler, limit) {
  let sequence = [].concat(urls); // 复制urls
  // 这一步是为了初始化 promises 这个"容器"
  let promises = sequence.splice(0, limit).map((url, index) => {
    return handler(url).then(() => {
      // 返回下标是为了知道数组中是哪一项最先完成
      return index;
    });
  });
  // 注意这里要将整个变量过程返回,这样得到的就是一个Promise,可以在外面链式调用
  // 这里的sequence是已经被截去前三个后剩下的数组
  return sequence.
    .reduce((resolve, url) => {
      return resolve.then(() => {
          return Promise.race(promises); // 返回已经完成的下标
        })
        .then(fastestIndex => { // 获取到已经完成的下标
        	// 将"容器"内已经完成的那一项替换
          promises[fastestIndex] = handler(url).then(() => {
              return fastestIndex; // 要继续将这个下标返回,以便下一次变量
            }
          );
        })
        .catch(err => {
          console.error(err);
        });
    }, Promise.resolve()) // 初始化传入
    .then(() => { // 最后三个用.all来调用
      return Promise.all(promises);
    });
}
limitLoad(urls, loadImg, 3)
  .then(res => {
    console.log("图片全部加载完毕");
    console.log(res);
  })
  .catch(err => {
    console.error(err);
  });







// 关键
let promises = sequence.splice(0, limit).map((url, index) => {
  return handler(url).then(() => {
    return index
  })
})


sequence.reduce(((resolve, url) => {
  return resolve.then(() => {
    return Promise.race(promises)
  }.then(finishIndex => {
    promises[finishIndex] = handler(url).then(() => {
      return finishIndex
    })
  }))
}, Promise.resolve()).then(() => Promise.all(promises))

  • 实现 finally
Promise.prototype.finally = function(onFinally) {
	return this.then(
		/* onFulfilled */
		res => Promise.resolve(onFinally()).then(() => res),
		/* onRejected */
		err =>
			Promise.resolve(onFinally()).then(() => {
				throw err;
			})
	);
};
  • 实现 Promise.all
Promise.all = function(promises) {
	return new Promise(function(resolve, reject) {
		var resolvedCounter = 0;
		var promiseNum = promises.length;
		var resolvedValues = new Array(promiseNum);
		for (var i = 0; i < promiseNum; i++) {
			// 自执行函数传入i
			(function(i) {
				Promise.resolve(promises[i]).then(
					function(value) {
						resolvedCounter++;
						resolvedValues[i] = value;
						if (resolvedCounter == promiseNum) {
							return resolve(resolvedValues);
						}
					},
					function(reason) {
						return reject(reason);
					}
				);
			})(i);
		}
	});
};
  • Promise 的错误捕获 当 promise 的状态为 rejected 且未对 promise 对象使用 catch 方法,此时的异常信息会被 promise 对象吃掉 可以通过监听 unhandledRejection 事件,专门监听未捕获的 reject 错误。
// 浏览器下
window.addEventListener("unhandledrejection", e => {
	e.preventDefault();
	console.log(e);
});
基础输出顺序题
new Promise((resolve, reject) => {
	console.log(3);
	let p = new Promise((resolve, reject) => {
		console.log(7);
		setTimeout(() => {
			console.log(5);
			resolve(6);
		}, 0);
		resolve(1);
	});
	resolve(2);
	p.then(arg => {
		console.log(arg);
	});
}).then(arg => {
	console.log(arg);
});
console.log(4);

// 3 7 4 1 2 5
Promise.resolve().then(() => {
	console.log("promise1");
	const timer2 = setTimeout(() => {
		console.log("timer2");
	}, 0);
});
const timer1 = setTimeout(() => {
	console.log("timer1");
	Promise.resolve().then(() => {
		console.log("promise2");
	});
}, 0);
console.log("start");

// 'start'
// 'promise1'
// 'timer1'
// 'promise2'
// 'timer2'
const promise1 = new Promise((resolve, reject) => {
	setTimeout(() => {
		resolve("success");
	}, 1000);
});
const promise2 = promise1.then(() => {
	throw new Error("error!!!");
});
console.log("promise1", promise1);
console.log("promise2", promise2);
setTimeout(() => {
	console.log("promise1", promise1);
	console.log("promise2", promise2);
}, 2000);

// 'promise1' Promise{<pending>}
// 'promise2' Promise{<pending>}
// test5.html:102 Uncaught (in promise) Error: error!!! at test.html:102
// 'promise1' Promise{<resolved>: "success"}
// 'promise2' Promise{<rejected>: Error: error!!!}
const promise1 = new Promise((resolve, reject) => {
	setTimeout(() => {
		resolve("success");
		console.log("timer1");
	}, 1000);
	console.log("promise1里的内容");
});
const promise2 = promise1.then(() => {
	throw new Error("error!!!");
});
console.log("promise1", promise1);
console.log("promise2", promise2);
setTimeout(() => {
	console.log("timer2");
	console.log("promise1", promise1);
	console.log("promise2", promise2);
}, 2000);

// 'promise1里的内容'
// 'promise1' Promise{<pending>}
// 'promise2' Promise{<pending>}
// 'timer1'
// test5.html:102 Uncaught (in promise) Error: error!!! at test.html:102
// 'timer2'
// 'promise1' Promise{<resolved>: "success"}
// 'promise2' Promise{<rejected>: Error: error!!!}
Promise.resolve()
	.then(() => {
		return new Error("error!!!");
	})
	.then(res => {
		console.log("then: ", res);
	})
	.catch(err => {
		console.log("catch: ", err);
	});

// "then: " "Error: error!!!"

// 返回任意一个非 promise 的值都会被包裹成 promise 对象,因此这里的return new Error('error!!!')也被包裹成了return Promise.resolve(new Error('error!!!'))。
// 当然如果你抛出一个错误的话,可以用下面👇两的任意一种:
// return Promise.reject(new Error('error!!!'));
// 或者
// throw new Error('error!!!')
async function async1() {
	console.log("async1 start");
	await new Promise(resolve => {
		console.log("promise1");
	});
	console.log("async1 success");
	return "async1 end";
}
console.log("script start");
async1().then(res => console.log(res));
console.log("script end");

// 'script start'
// 'async1 start'
// 'promise1'
// 'script end'

// 在async1中await后面的Promise是没有返回值的,也就是它的状态始终是pending状态,所以在await之后的内容是不会执行的,也包括async1后面的 .then。
async function async1() {
	console.log("async1 start");
	await new Promise(resolve => {
		console.log("promise1");
		resolve("promise1 resolve");
	}).then(res => console.log(res));
	console.log("async1 success");
	return "async1 end";
}
console.log("script start");
async1().then(res => console.log(res));
console.log("script end");

// 'script start'
// 'async1 start'
// 'promise1'
// 'script end'
// 'promise1 resolve'
// 'async1 success'
// 'async1 end
async function async1() {
	console.log("async1 start");
	await async2();
	console.log("async1 end");
}

async function async2() {
	console.log("async2");
}

console.log("script start");

setTimeout(function() {
	console.log("setTimeout");
}, 0);

async1();

new Promise(function(resolve) {
	console.log("promise1");
	resolve();
}).then(function() {
	console.log("promise2");
});
console.log("script end");

// 'script start'
// 'async1 start'
// 'async2'
// 'promise1'
// 'script end'
// 'async1 end'
// 'promise2'
// 'setTimeout'
async function async1() {
	await async2();
	console.log("async1");
	return "async1 success";
}
async function async2() {
	return new Promise((resolve, reject) => {
		console.log("async2");
		reject("error"); // 如果改为throw new Error也是一样的
	});
}
async1().then(res => console.log(res));

// 'async2'
// Uncaught (in promise) error
综合输出顺序题
const first = () =>
	new Promise((resolve, reject) => {
		console.log(3);
		let p = new Promise((resolve, reject) => {
			console.log(7);
			setTimeout(() => {
				console.log(5);
				resolve(6);
				console.log(p);
			}, 0);
			resolve(1);
		});
		resolve(2);
		p.then(arg => {
			console.log(arg);
		});
	});
first().then(arg => {
	console.log(arg);
});
console.log(4);

// 3
// 7
// 4
// 1
// 2
// 5
// Promise{<resolved>: 1}

const async1 = async () => {
	console.log("async1");
	setTimeout(() => {
		console.log("timer1");
	}, 2000);
	await new Promise(resolve => {
		// await后面的Promise是没有返回值的,也就是它的状态始终是pending状态,await之下的语句都不会执行
		console.log("promise1");
	});
	console.log("async1 end");
	return "async1 success";
};
console.log("script start");
async1().then(res => console.log(res));
console.log("script end");
Promise.resolve(1)
	.then(2)
	.then(Promise.resolve(3))
	.catch(4)
	.then(res => console.log(res));
setTimeout(() => {
	console.log("timer2");
}, 1000);

// 'script start'
// 'async1'
// 'promise1'
// 'script end'
// 1
// 'timer2'
// 'timer1'
// 注意定时器的延迟时间

// 注意这道题最后p1的返回值!!!!!!!!!!!!
const p1 = new Promise(resolve => {
	setTimeout(() => {
		resolve("resolve3");
		console.log("timer1");
	}, 0);
	resolve("resolve1");
	resolve("resolve2");
})
	.then(res => {
		console.log(res);
		setTimeout(() => {
			console.log(p1);
		}, 1000);
	})
	.finally(res => {
		console.log("finally", res);
	});

// 'resolve1'
// 'finally' undefined
// 'timer1'
// Promise{<resolved>: undefined} // 这里最后输出的返回值,就是finally的返回值undefined
const arr = [1, 2, 3];
arr.reduce(
	(p, x) =>
		p.then(
			() =>
				new Promise(resolve => {
					setTimeout(() => resolve(console.log(x), 1000));
				})
		),
	Promise.resolve()
);

【建议星星】要就来 45 道 Promise 面试题一次爽到底(1.1w 字用心整理) (opens new window) 「ES6 系列」彻底弄懂 Promise (opens new window)

Last Updated: 7/6/2023, 9:02:37 PM