Promise 解决了哪些问题
-
多个异步请求并发的问题 Promise.all;
-
解决异步请求中的回调地狱问题 (async await 或者 then 链 或者 generate + co);
function ajax1() { return new Promise(resolve => { $.ajax({ url: '/student', method: 'get', data: { class: 1 }, success: resolve }); }); } function ajax2(arr) { return new Promise(resolve => { $.ajax({ url: '/score', method: 'get', data: { stuId: arr }, success: resolve }); }); } function ajax3() { return new Promise(resolve => { $.ajax({ url: '/jige', // ... success: resolve }); }); } // 第一种实现 ajax1().then(result => { return ajax2(result.map(item => item.id)); }).then(result => { return ajax3(); }).then(result => { // ... }); // 第二种实现 async function handle() { let result = await ajax1(); result = await ajax2(result.map(item => item.id)); result = await ajax3(); // 此处的 result 就是三次异步请求后获取的信息 } handle();
Promise 规范
-
Promise 是用来管理异步编程的,它本身不是异步的,new Promise 的时候会立即执行 executor 函数,只不过一般会在 executor 函数中处理一个异步操作;
-
new Promise(executor(resolve, reject)=>{ }):executor 函数的两个参数 resolve 回调 和 reject 回调 的执行都是异步微任务;
Promise 状态
-
Promise 本身有三个状态:
- pending:初始状态,等待态;
- fulfilled:操作成功完成 (resolve 执行,PromiseStatus 会从 pending -> fulfilled);
- rejected:操作失败 (reject 执行,PromiseStatus 会从 pending -> rejected);
-
执行 resolve/reject 函数,执行这两个函数中的一个,都可以修改 Promise 的 [[PromiseStatus]]、[[PromiseValue]] ,一旦状态被改变,再执行 resolve、reject 就没有用了;
let p1 = new Promise((resolve, reject) => { setTimeout(_ => { // 一旦状态被改变,再次执行 resolve、reject 就没有用了 resolve('ok'); reject('no'); }, 200); }); setTimeout(()=>{ console.log('s', p1); // Promise { 'ok' } }, 1000);
Promise 的链式调用
catch 方法
-
.catch(fn) <== 等价 ==> .then(null, onRejected);
-
示例代码:
JavaScriptJavaScriptPromise.resolve(10).then(result => { console(a);//=>报错了 }).catch(reason => { console.log(`失败:${reason}`); });
Promise.resolve(10).then(result => { console(a);//=>报错了 }).then(null, reason => { console.log(`失败:${reason}`); });
then 方法
-
Promise.prototype.then([resolvedFn], [rejectedFn])
- resolvedFn:成功执行的方法;
- rejectedFn:失败执行的方法;
-
示例代码:
// new Promise 的时候先执行 executor 函数 let p1 = new Promise((resolve, reject) => { setTimeout(_ => { let ran = Math.random(); console.log(ran); if (ran < 0.5) { reject('NO!'); return; } resolve('OK!'); }, 1000); }); // p1.then 基于 then 方法,存储起来两个函数 // executor 中的异步操作结束,resolve/reject 控制 Promise 状态,决定执行 then 存储函数中的某一个 p1.then(result => { // 成功:ok console.log(`成功:` + result); }, reason => { // 失败:no console.log(`失败:` + reason); });
-
为什么很多人说 promise 是异步的?
// resolve/reject 的执行,不论是否放到一个异步操作中,都需要等待 then 先执行完 // 因为此时 then 还没有执行,没有存储 then 中的操作,resolve/reject 没办法执行 then 中对应的操作 // 此处是一个异步操作(所以很多人说 promise 是异步的),而且是微任务操作 let p1 = new Promise((resolve, reject) => { resolve(100); }); p1.then(result => { console.log(`成功:` + result); }, reason => { console.log(`失败:` + reason); }); console.log(3); // 输出: // 3 // 成功:100
then 链机制
-
每次执行完 promsie.then 之后返回的都是新的 promise 实例 (不能返回 this ,因为状态一经修改就不允许改变了,then 链的状态值有可能会变化);
-
新任务的状态取决于后续处理:
- 若没有相关的后续处理,新任务的状态和前任务一致,数据为前任务的数据;
- 若有后续处理但还未执行,新任务挂起;
- 若后续处理执行了,则根据后续处理的情况确定新任务的状态;
- 后续处理执行无错,新任务的状态为完成,数据为后续处理的返回值;
- 后续处理执行有错,新任务的状态为失败,数据为异常对象;
- 后续执行后返回的是一个任务对象,新任务的状态和数据与该任务对象一致;
-
then 链 练习
JavaScriptJavaScriptJavaScriptJavaScriptPromise.resolve(10) .then( (result) => { console.log(`成功:${result}`); // 返回值是 promise,则根据 promise 的状态来执行 then 的 成功/失败 回调; return Promise.reject(result * 10); }, ) .then( (result) => console.log(`成功:${result}`), (reason) => console.log(`失败:${reason}`) ); // 成功:10 // 失败:100
let p1 = new Promise((resolve, reject) => resolve(100)); let p2 = p1.then( (result) => { console.log("成功:" + result); result + 100; // 没有写 return,执行下一次 then 的成功回调函数,并将 undefinded 传入; } ); let p3 = p2.then( (result) => { console.log("成功:" + result); }, (reason) => { console.log("失败:" + reason); } ); // 成功:100 // 成功:undefined
new Promise((resolve, reject) => { // 如果是代码报错的情况,一定会执行下一次 then 的失败回调函数 throw new Error("代码错误"); }) .then( (result) => { console.log(`成功:${result}`) }, (reason) => { console.log(`失败:${reason}`) } ); // 失败:Error: 代码错误
Promise.reject(10) .then(result => { // 错误处理,如果距离自己最近的 then 没有错误处理,则向下查找 console.log(`成功:${result}`); return result * 10; }) .then(null, reason => console.log(`失败:${reason}`)); // 失败:10
then 和 catch 的区别
-
如果在 then 的第一个函数里抛出了异常,后面的 catch 能捕获到,而当前 then 的第二个函数捕获不到;
-
then 的第二个参数和 catch 捕获错误信息的时候会就近原则,如果是 promise 内部报错,reject 抛出错误后:
JavaScriptJavaScript/** * 示例一:then 的第二个参数和 catch 方法都存在的情况下,只有 then 的第二个参数能捕获到 */ Promise.resolve(10) .then((res) => { console(a); // 报错了 }) .then( (res) => { console.log(`成功:${res}`); }, (reason) => { console.log(`失败1:${reason}`); } ) .catch((reason) => { console.log(`失败2:${reason}`); }); // 失败1:ReferenceError: a is not defined
/** * 示例二:如果 then 的第二个参数不存在,则 catch 方法会捕获到 */ Promise.resolve(10) .then((result) => { console(a); // 报错了 }) .then((res) => { console.log(`失败1:${reason}`); }) .catch((reason) => { console.log(`失败2:${reason}`); }); // 失败2:ReferenceError: a is not defined
Promise 中的静态方法
方法名 | 含义 |
---|---|
Promise.resolve(‘100’) | 等价于 new Promise(resolve=> resolve(‘100’)) |
Promise.reject(‘error’) | 等价于 new Promise((, reject) => reject(‘error’)) |
Promise.all([p1, p2]) | · 只有 p1、p2 的状态都变成 fulfilled ,promise 的状态才会变成 fulfilled ,此时 p1、p2 的返回值组成一个数组, 传递给 promise 的回调函数; · 只要 p1、p2 之中有一个被 rejected ,promise 的状态就变成 rejected ,此时第一个被 reject 的实例的返回值, 会传递给 promise 的回调函数; |
Promise.any([p1, p2]) | 返回一个任务,任务数组任一成功则成功,任务全部失败则失败 |
Promise.race([p1, p2]) | 只要 p1、p2 之中有一个实例率先改变状态,promise 的状态就跟着改变,值传递给 promise 的回调函数 |
Promise.allSettled([p1, p2]) | 返回一个任务,任务数组全部已决则成功,该任务不会失败 |
案例
中断 Promise
const wrap = (promise) => {
let abort;
// 实现一个 promise,之后可控制该 promsie 来控制 race
let myP = new Promise((resolve, reject) => {
abort = reject;
});
let p = Promise.race([promise, myP]);
p.abort = abort;
return p;
};
// 测试
let promise = new Promise((resolve, reject) => {
setTimeout(() => resolve("ok 成功了"), 10000);
});
let p = wrap(promise);
p.then(
(data) => console.log(data),
(err) => console.log(err)
);
// 需求:2s 后中断请求
setTimeout(() => p.abort("promise 超时"), 2000);
中断 then 链
Promise.resolve(100)
.then((data) => {
return new Promise(() => {}); // 直接返回一个 new Promise,中断 then 链
})
.then(
(data) => {
console.log(data);
},
(err) => {
console.log(err);
}
);
defer 的作用
-
作用:deferred 对象实现了 Promise 并且将 resolve 和 reject 方法暴露在了构造函数外面,Promise 对象的状态更为灵活;状态的改变只有一次,之后的更改会忽略;
let deferred = {}; deferred.promise = new Promise((resolve, reject) => { deferred.resolve = resolve; deferred.reject = reject; }); setTimeout(() => deferred.resolve(1), 1000); deferred.promise.then((res) => console.log("ok:", res)); // ok: 1
-
使用场景:
JavaScriptJavaScriptJavaScript/** * 减少代码嵌套 */ let fs = require("fs"); // 实现 promise 延迟对象 defer let Deferred = function () { let dfd = {}; dfd.promise = new Promise((resolve, reject) => { dfd.resolve = resolve; dfd.reject = reject; }); return dfd; }; // function read() { // return new Promise((resolve, reject) => { // fs.readFile('./a.txt', 'utf8', (err, data) => { // if (!err) resolve(data) // }) // }) // } // 减少代码嵌套 function read() { let defer = Deferred(); fs.readFile("./a.txt", "utf8", (err, data) => { if (!err) defer.resolve(data); }); return defer.promise; } read().then((data) => console.log(data));
/** * 多个地方想控制 1 个 Promise 的状态,回调只想执行 1 次 */ let deferred = {}; deferred.promise = new Promise((resolve, reject) => { deferred.resolve = resolve; deferred.reject = reject; }); // 异步操作的顺序不确定 setTimeout(() => deferred.resolve(), 10 * Math.random()); setTimeout(() => deferred.resolve(), 10 * Math.random()); // 只要有 1 个异步操作完成就执行回调 deferred.promise.then(() => console.log("ok"));
/** * 多个异步之间的协作方案,多个延迟对象配合使用 */ let Deferred = function () { let dfd = {}; dfd.promise = new Promise((resolve, reject) => { dfd.resolve = resolve; dfd.reject = reject; }); return dfd; }; let d1 = Deferred(); let d2 = Deferred(); Promise.all([d1.promise, d2.promise]).then((res) => { console.log(res); // [ 'Fish', 'Pizza' ] }); d1.resolve("Fish"); d2.resolve("Pizza");
面试题
看程序写结果
Promise.resolve(1).then((res) => {
console.log(res)
return 2
}).catch((err) => {
return 3
}).then((res) => {
console.log(res)
})
// 1
// 2
// 第一个 then 中没有异常,catch 捕获不到异常,则执行第二个 then
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>}
// promise1~ Promise {<fulfilled>: 'success'}
// promise2~ Promise {<rejected>: Error: error!!!}
// promise1、promise2 都是微任务执行,此时等待返回结果,状态为 pending
// 2s 后再次输出 promise1、promise2,此时微任务执行完毕,状态更新
setTimeout(() => console.log(5), 0);
new Promise(resolve => {
console.log(1);
resolve(3);
Promise.resolve().then(() => console.log(4))
}).then(num => {
console.log(num)
});
console.log(2);
// 1
// 2
// 4
// 3
// 5
// promise 构造函数里面是同步代码,构造函数的 resolve/reject 是异步的
// Promise.resolve().then 也是异步,此行代码先于 resolve 回调
Promise.resolve()
.then(() => {
return new Error('error!!!')
})
.then((res) => {
console.log('then: ', res)
})
.catch((err) => {
console.log('catch: ', err)
})
// then: Error: error!!!
// return 一个 error 对象并不会抛出错误,所以不会被后续的 catch 捕获
// 改成下面这样会被捕获异常
// return Promise.reject(new Error(‘error!!!’))
// throw new Error(‘error!!!’)
Promise.resolve(1)
.then(2)
.then(Promise.resolve(3))
.then(console.log)
// 1
// .then 或者 .catch 的参数期望是函数,传入非函数则会发生值透传
// 链式调用的参数不是函数,会发生值透传,传入的非函数值会被忽略
const first = () => (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);
});
}));
first().then((arg) => {
console.log(arg);
});
console.log(4);
// 3
// 7
// 4
// 1
// 2
// 5
var p = new Promise((resolve, reject) => {
reject(Error('The Fails!'))
})
p.catch(error => console.log(error.message))
p.catch(error => console.log(error.message))
// The Fails!
// The Fails!
var p = new Promise((resolve, reject) => {
// 创建并返回一个被拒绝(rejected)的 Promise 对象,在这个 Promise 对象被拒绝时,它将抛出一个包含错误信息的 Error 对象
return Promise.reject(new Error('The Fails!'))
})
p.catch(error => console.log(error.message))
p.catch(error => console.log(error.message))
// Error: The Fails!
// at /Users/wushuai/Desktop/javascript/1.js:2:27
// at new Promise (<anonymous>)
// at Object.<anonymous> (/Users/wushuai/Desktop/javascript/1.js:1:9)
// at Module._compile (node:internal/modules/cjs/loader:1254:14)
// at Module._extensions..js (node:internal/modules/cjs/loader:1308:10)
// at Module.load (node:internal/modules/cjs/loader:1117:32)
// at Module._load (node:internal/modules/cjs/loader:958:12)
// at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)
// at node:internal/main/run_main_module:23:47
async function async1() {
console.log(1);
const result = await async2();
console.log(3);
}
async function async2() {
console.log(2);
}
Promise.resolve().then(() => {
console.log(4);
});
setTimeout(() => {
console.log(5);
});
async1();
console.log(6);
// 1
// 2
// 6
// 4
// 3
// 5
Promise.resolve('Success!').then(() => {
throw Error('Oh noes!')
}).catch(error => {
console.log('actually, that worked');
return 'actually, that worked'
}).then(data => {
throw Error('The fails!')
}).catch(error => console.log(error.message))
// actually, that worked
// The fails!
new Promise((resolve, reject) => {
resolve('Success!')
}).then(() => {
throw Error('Oh noes!')
}).catch(error => {
console.log('catch=>' + error)
return "actually, that worked"
}).catch(error => console.log(error.message))
// catch=>Error: Oh noes!
var p = new Promise((resolve, reject) => {
reject(Error('The Fails!'))
})
.catch(error => console.log('catch=>' + error))
.then(error => console.log('then=>' + error))
// catch=> Error: The Fails!
// then => undefined
封装一个延时微任务
// 通过 async 封装
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
async function test() {
var temple = await sleep(1000);
console.log(1111);
return temple;
}
test();
// 通过 generate 来实现
function* sleep(ms) {
yield new Promise((resolve, reject) => setTimeout(resolve, ms));
}
sleep(500).next().value.then(function () {
console.log(2222)
});
面试题(临时)
上一篇