单线程和异步
JS 是单线程语言,在同一时刻只能执行一个任务。
JS 代码分为同步代码和异步代码:
- 同步代码是顺序执行的。JS 引擎只有一个主线程,按照编写的顺序依次执行代码
1 2 3 4
| console.log(1); console.log(2); console.log(3);
|
- 异步代码是异步执行的。异步代码不会阻塞主线程,而是被添加到一个任务队列中,当主线程执行完同步代码后,会从任务队列中取出一个异步任务并执行
1 2 3 4 5 6 7 8 9
| console.log(1);
setTimeout(() => { console.log(456); }, 0);
console.log(2); console.log(3);
|
异步任务包含三种:
- 定时器(
setTimeout、setInterval)
- 网络事件触发(
onload、onerror 等)
- 鼠标键盘事件触发(
onclick、onkeydown 等)
处理异步任务的方法:
- 回调函数是 JS 中处理异步任务最常用的方法,当异步任务执行完毕后,会调用回调函数
- Promise 是解决“回调地狱”问题的更优雅的方法,使代码更加简洁易读
- Generator 是一种解决异步编程的特殊函数
- async 函数使得异步操作更加方便,是 Generator 函数的语法糖
异步的应用场景
1 2 3 4 5 6
| console.log('start'); $.get('./data1.json', function (data1) { console.log(data1); }) console.log('end');
|
1 2 3 4 5 6 7 8
| console.log('start'); let img = document.createElement('img'); img.onload = function () { console.log('loaded'); } img.src = '/xxx.png'; console.log('end');
|
- 定时任务,如 setTimeout、setInterval
1 2 3 4 5 6
| console.log(100); setTimeout(function () { console.log(200); }, 1000) console.log(300);
|
1 2 3 4 5 6
| console.log(100); setInterval(function () { console.log(200); }, 1000) console.log(300);
|
Event Loop
异步是基于回调实现的,Event Loop 又称事件循环或事件轮询,是异步回调的实现原理。
Event Loop 的执行过程
- 同步代码,一行一行依次放入
Call Stack(调用栈)中执行
- 遇到异步,先“记录”,等待时机(定时、网络请求等)
- 时机到了,就移动到
Callback Queue(任务队列)
- 如
Call Stack 为空(即同步代码执行完毕),则先尝试 DOM 渲染,再触发 Event Loop
- 轮询查找
Callback Queue,如有,则移动到 Call Stack 执行
- 继续轮询查找,如此反复
1 2 3 4 5 6 7 8
| console.log('Hi');
setTimeout(function callback1() { console.log('cb1'); }, 5000);
console.log('Bye');
|
上述示例的运行过程为:
- 将同步代码
console.log('Hi'); 推入 Call Stack 中进行执行,并在 Browser Console 中显示打印结果 'Hi',执行完毕则在 Call Stack 中移除
- 将异步的定时器函数
setTimeout 放入 Web APIs 中,并开始计时
- 将同步代码
console.log('Bye'); 推入 Call Stack 中进行执行,并在 Browser Console 中显示打印结果 'Bye',执行完毕则在 Call Stack 中移除
- 此时同步代码全部执行完毕,Call Stack 空置
- 5000ms 后,将异步函数移入 Callback Queue 中,即刻启动 Event Loop
- 将异步函数推入 Call Stack 中执行函数体,在 Browser Console 中显示打印结果
'cb1',执行完毕将其移出 Call Stack
DOM 事件与 Event Loop 的关系
- 异步(setTimeout、AJAX 等)使用回调,基于 Event Loop
- DOM 事件也使用回调,基于 Event Loop
Promise
Promise 的基本使用
callback hell 示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| $.get(url1, (data1) => { console.log(data1); $.get(url2, (data2) => { console.log(data2); $.get(url3, (data3) => { console.log(data3); }) }) })
|
promise 解决 callback hell 问题:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| function getData(url) { return new Promise((resolve, reject) => { $.ajax({ url, success(data) { resolve(data); }, error(err) { reject(err); } }) }) }
const url1 = '/data1.json'; const url2 = '/data2.json'; const url3 = '/data3.json';
getData(url1).then(data1 => { console.log(data1); return getData(url2); }).then(data2 => { console.log(data2); return getData(url3); }).then(data3 => { console.log(data3); }).catch(err => { console.error(err); })
|
使用 promise 加载图片示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| function loadImg(src) { return new Promise((resolve, reject) => { const img = document.createElement('img'); img.onload = () => { resolve(img); } img.onerror = () => { const err = new Error(`图片加载失败 ${src}`) reject(err); } img.src = src; }) }
const url1 = 'https://albertbobo.github.io/images/avatar.png'; const url2 = 'https://img2.sycdn.imooc.com/545847e20001163c02200220-140-140.jpg';
loadImg(url1).then(img1 => { document.body.appendChild(img1); console.log('图片1已加载'); console.log('图片1的宽度:', img1.width); return img1; }).then(img1 => { console.log('图片1的高度:', img1.height); return loadImg(url2); }).then(img2 => { document.body.appendChild(img2); console.log('图片2已加载'); }).catch(ex => { console.error(ex); })
|
Promise 的三种状态
一个 Promise 必然存在以下三种状态之一:
pending(待定):初始状态,既没有被兑现,也没有被拒绝
fulfilled(已兑现):意味着操作成功完成
rejected(已拒绝):意味着操作失败
两种方法将 Promise 的状态由 pending 转化为 fulfilled 或者 rejected,且变化是不可逆的:
- pending -> resolve() 方法 -> fulfilled
- pending -> reject() 方法 -> rejected
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| const p1 = new Promise((resolve, reject) => { }) console.log('p1: ', p1);
const p2 = new Promise((resolve, reject) => { setTimeout(() => { resolve(); }) }) console.log('p2: ', p2); setTimeout(() => console.log('p2-setTimeout: ', p2));
const p3 = new Promise((resolve, reject) => { setTimeout(() => { reject(); }) }) console.log('p3: ', p3); setTimeout(() => console.log('p3-setTimeout: ', p3));
|
Promise 的方法
Promise.prototype.then()
Promise 并不直接返回最后的结果,而是将它们放在 resolve() 方法或 reject() 方法里面,使用 then() 方法获取异步回调的值。
then() 方法是定义在原型对象 Promise.prototype 上的
then() 方法的第一个参数是 fulfilled 状态的回调函数,第二个参数是 rejected 状态的回调函数,它们都是可选的
注意!then() 方法里面的两个参数虽然都是可选的,但是如果 Promise 返回的是 rejected 状态,而且需要调用它的值,就必须设置两个参数,在第二个参数上调用 rejected 的值。如果是 fulfilled 状态,第二个参数可以省略。
then() 方法返回的是一个新的 Promise 实例,因此可以使用链式写法,即 then() 方法后面再调用另一个 then() 方法,这是解决回调地狱的关键
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| new Promise((resolve, reject) => { resolve(); }).then(() => { console.log('ok 1'); }).then(() => { console.log('ok 2'); })
let p1 = new Promise((resolve, reject) => { resolve(); })
let p2 = p1.then(() => { console.log('ok 1'); })
let p3 = p2.then(() => { console.log('ok 2'); })
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| new Promise((resolve, reject) => { reject('error'); }).then(() => { console.log('ok 1'); }, err => { console.error('error 1: ' + err); }).then(() => { console.log('ok 2'); })
let p1 = new Promise((resolve, reject) => { reject('error'); })
let p2 = p1.then(() => { console.log('ok 1'); }, function fn2(err) { console.error('error 1: ' + err); })
let p3 = p2.then(() => { console.log('ok 2'); })
|
Promise.prototype.catch()
catch() 方法是 then(null, rejection) 或 then(undefined, rejection) 的别名,用于指定发生错误时的回调函数。
- 如果异步操作抛出错误,状态就会变为
rejected,就会调用 catch() 方法指定的回调函数来处理这个错误
- Promise 对象应该调用
catch()方法,以便处理 Promise 内部发生的错误
catch() 方法返回的还是一个 Promise 对象,因此后面还可以接着调用 then() 方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| new Promise((resolve, reject) => { resolve(); }).then(() => { throw new Error('then error 1'); }).catch(err => { console.error('catch error 1: ' + err); }).then(() => { console.log('ok 1'); }, err => { console.log('error 1'); })
|
1 2 3 4 5 6 7 8 9 10 11 12
| new Promise((resolve, reject) => { reject('error'); }).then(() => { console.log('ok 1'); }).then(() => { console.log('ok 2'); }).catch(err => { console.error('catch error 1: ' + err); })
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| new Promise((resolve, reject) => { reject('error'); }).then(() => { console.log('ok 1'); }).then(() => { console.log('ok 2'); }, err => { console.error('error 1: ' + err); throw 'error 2'; }).catch(err => { console.error('catch error: ' + err); })
|
总结:
- 错误在传递的过程中如果被中途“处理”(触发 then 的第二个参数,或者被 catch),则不再继续传递;否则继续传递,直到被处理
- 错误被“处理”之后,过程中的 Promise 对象全部为
fulfilled 状态
then(), catch() 小结
Promise 状态的表现:
pending 状态,不会触发 then() 和 catch() 方法
fulfilled 状态,会触发 then() 方法的第一个回调函数
rejected 状态,会触发 then() 方法的第二个回调函数,或者触发 catch() 方法的回调函数
1 2 3 4 5 6 7 8 9
| const p1 = Promise.resolve(100); p1.then(data => { console.log('data1: ', data); }).catch(err => { console.error('err1: ' + err); })
|
1 2 3 4 5 6 7 8 9
| const p2 = Promise.reject('err'); p2.then(data => { console.log('data2: ', data); }).catch(err => { console.error('catch err2: ' + err); })
|
1 2 3 4 5 6 7 8 9 10 11
| const p3 = Promise.reject('err'); p3.then(data => { console.log('data3: ', data); }, err => { console.error('err3: ' + err); }).catch(err => { console.error('catch err3: ' + err); })
|
then 和 catch 方法对状态的改变:
- then() 方法在正常情况下返回
fulfilled,如果里面有错误则返回 rejected
1 2 3 4 5 6 7 8 9 10 11 12
| const p1 = Promise.resolve().then(() => { return 100; });
console.log(p1);
p1.then(() => { console.log('ok 1'); })
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| const p2 = Promise.resolve().then(() => { throw new Error('then error'); });
console.log(p2);
p2.then(() => { console.log('ok 2'); }).catch(err => { console.error('catch error 2: ' + err); })
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| const p3 = Promise.resolve().then(() => { const x = 'a'; x = 'b'; });
console.log(p3);
p3.then(() => { console.log('ok 3'); }).catch(err => { console.error('catch error 3: ' + err); })
|
- catch() 方法在正常情况下返回
fulfilled,如果里面有错误则返回 rejected
1 2 3 4 5 6 7 8 9 10 11 12
| const p1 = Promise.reject('my error').catch(err => { console.error(err); })
console.log(p1);
p1.then(() => { console.log('ok 1'); })
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| const p2 = Promise.reject('my error').catch(err => { throw new Error('catch err: ' + err); })
console.log(p2);
p2.then(() => { console.log('ok 2'); }).catch(err => { console.error('catch error 2: ' + err); })
|
Promise.prototype.finally()
finally() 方法用于指定不管 Promise 对象最后状态如何,都会执行的操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
| new Promise((resolve, reject) => { reject('error'); }).then(() => { console.log('ok 1'); }, err => { console.log('error 1:' + err); }).then(() => { console.log('ok 2'); }, err => { console.log('error 2:' + err); }).then(() => { console.log('ok 3'); }, err => { console.log('error 3:' + err); }).catch(err => { console.log('catch 1:' + err); }).finally(() => { console.log('finally 1'); }).then(() => { console.log('ok 4'); }, err => { console.log('error 4:' + err); }).finally(() => { console.log('finally 2'); }).catch(err => { console.log('catch 2:' + err); })
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| const promise = new Promise((resolve, reject) => { resolve('ok!'); setTimeout(() => { reject('oops!'); }, 500); })
promise.then(val => { console.log('success: ' + val); }).catch(err => { console.error('error: ' + err); }).finally(() => { console.log('finally'); })
|
Promise.all()
all() 方法用于将多个 Promise 实例包装成一个新的 Promise 实例。其参数为一个可迭代的对象(例如:Array, Map, Set),返回值是一个 Promise 实例。
let p = Promise.all([p1, p2, p3]);
p 的状态由 p1、p2、p3 决定,分为两种情况:
- 只有 p1、p2、p3 的状态都变成
fulfilled,p 的状态才会变成 fulfilled,此时 p1、p2、p3 的返回值组成一个数组,传递给 p 的回调函数
- 只要 p1、p2、p3 中有一个被
rejected,p 的状态就变成 rejected,此时第一个被 rejected 的实例的返回值会传递给 p 的回调函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| const p1 = new Promise((resolve, reject) => { resolve('ok 1'); });
const p2 = new Promise((resolve, reject) => { setTimeout(() => { resolve('ok 2'); }, 1000) });
const p3 = new Promise((resolve, reject) => { reject('oops 1'); });
const p4 = new Promise((resolve, reject) => { reject('oops 2'); });
Promise.all([p1, p2]).then(result => { console.log(result); }).catch(err => { console.error(err); })
Promise.all([p1, p2, p3, p4]).then(result => { console.log(result); }).catch(err => { console.error('error: ' + err); })
|
注意!如果作为参数的 Promise 实例被 rejected,但是自身定义了 catch() 方法且没有错误,则此时状态变为 fulfilled,那么并不会触发 Promise.all() 的 catch() 方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| const p1 = new Promise((resolve, reject) => { resolve('ok 1'); }).then(val => val).catch(err => err);
const p2 = new Promise((resolve, reject) => { throw new Error('oops 1'); }).then(val => val).catch(err => err);
Promise.all([p1, p2]).then(result => { console.log(result); }).catch(err => { console.error(err); })
|
Promise.race()
race() 方法同样将一个 Promise 可迭代对象包装成一个新的 Promise 实例。
let p = Promise.race([p1, p2, p3]);
只要 p1、p2、p3 中有一个实例率先确定状态,p 的状态就随之确定,且该实例的返回值就是传递给 p 的回调函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| const p1 = new Promise((resolve, reject) => { setTimeout(() => { resolve('ok 1'); }, 1000) });
const p2 = new Promise((resolve, reject) => { setTimeout(() => { resolve('ok 2'); }, 500) });
const p3 = new Promise((resolve, reject) => { setTimeout(() => { reject('oops 1'); }, 500) });
Promise.race([p1, p2, p3]).then(result => { console.log(result); }).catch(err => { console.error('error: ' + err); })
|
async/await
async 函数返回一个 Promise 对象,可以使用 then 方法添加回调函数。在函数执行时,一旦遇到 await 就会先返回,等待异步操作完成,再接着执行函数体内后面的语法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| function loadImg(src) { return new Promise((resolve, reject) => { const img = document.createElement('img'); img.onload = () => { resolve(img); } img.onerror = () => { const err = new Error(`图片加载失败 ${src}`); reject(err); } img.src = src; }) }
const url1 = 'https://albertbobo.github.io/images/avatar.png'; const url2 = 'https://img2.sycdn.imooc.com/545847e20001163c02200220-140-140.jpg';
async function loadImg2() { const img2 = await loadImg(url2); return img2; }
(async function () { const img1 = await loadImg(url1); console.log(img1.height, img1.width); const img2 = await loadImg2(); console.log(img2.height, img2.width); })()
|
async/await 和 Promise 的关系
返回 Promise 对象
- 执行
async 函数返回的是一个 Promise 对象
async 函数内部 return 语句返回的值,会成为 then 方法回调函数的参数
1 2 3 4 5 6 7 8 9 10 11
| async function fn1() { return 100; }
const res1 = fn1();
console.log('res1: ', res1);
res1.then(data => { console.log(data); });
|
1 2 3 4 5 6 7 8 9 10 11
| async function fn2() { return Promise.resolve(200); }
const res2 = fn2();
console.log('res2: ', res2);
res2.then(data => { console.log(data); });
|
async 函数内部抛出错误会导致返回的 Promise 对象变为 rejected 状态,抛出的错误对象会被 catch 方法回调函数接收到
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| async function fn3() { throw new Error('oops!'); }
const res3 = fn3();
console.log('res3: ', res3);
res3.then(data => { console.log(data); }, err => { console.error(err); });
|
await 命令
1 2 3 4 5 6 7 8 9 10 11
| (async function () { const p = Promise.resolve(300); const data = await p; console.log(data); })();
(async function () { const p = Promise.resolve(300); p.then(data => console.log(data)); })();
|
1 2 3 4
| (async function () { const data = await fn2(); console.log(data); })();
|
1 2 3 4 5 6 7 8 9 10
| async function fn() { return 300; }
(async function () { const a = fn(); const b = await fn(); console.log(a); console.log(b); })();
|
- 正常情况下,
await 命令后面是一个 Promise 对象,如果不是,会被转成一个立即 resolve 的对象
1 2 3 4
| (async function () { const data = await 400; console.log(data); })();
|
await 命令后面的 Promise 对象如果为 rejected 状态,则 reject 的参数会被 catch 方法的回调函数接收到
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| (async function () { console.log('start'); const a = await 100; console.log('a: ', a); const b = await Promise.resolve(200); console.log('b: ', b); const c = await Promise.reject(300); console.log('c: ', c); console.log('end'); })();
|
1 2 3 4 5 6 7 8 9 10
| (async function fn4() { await Promise.reject('oops!'); })().then(data => { console.log(data); }).catch(err => { console.error(err); });
|
错误处理
- 如果
await 后面的异步操作出错,那么等同于 async 函数返回的 Promise 对象被 reject
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| async function fn5() { await new Promise((resolve, reject) => { throw new Error('oops!'); }) }
fn5().then(data => { console.log(data); }).catch(err => { console.error(err); });
|
- 捕获异常的方法是将其放在
try...catch代码块中,相当于 Promise 的 catch
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| async function fn5() { try { await new Promise((resolve, reject) => { throw new Error('oops!'); }); } catch (err) { console.error(err); } }
fn5();
|
1 2 3 4 5 6 7 8 9 10 11 12
| (async function () { const p = Promise.reject('oops!'); try { const res = await p; console.log(res); } catch (err) { console.error(err); } })();
|
- 如果有多个
await 命令,则可以统一放在 try...catch 结构中
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| (async function () { try { const val1 = await Promise.resolve(100); const val2 = await Promise.resolve(200); const val3 = await Promise.resolve(300); const res = val1 + val2 + val3; console.log('Final: ', res); } catch (err) { console.error(err); } })();
|
async/await 是语法糖
- async/await 是 Promise 的语法糖
- async/await 是消灭异步回调的终极武器,但是异步的本质仍然是回调函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
| async function async1() { console.log('async 1 start'); await async2(); console.log('ok 1'); await async3(); console.log('ok 2'); console.log('async 1 end'); }
async function async2() { console.log('async 2'); }
async function async3() { console.log('async 3'); }
console.log('script start'); async1(); console.log('script end');
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| async function async1() { console.log('async 1 start'); await async2(); console.log('async 1 end'); }
async function async2() { console.log('async 2 start'); await async3(); console.log('async 2 end'); }
async function async3() { console.log('async 3 start'); console.log('async 3 end'); }
async1();
|
宏任务与微任务
- 宏任务:setTimeout,setInterval,Ajax,DOM 事件
- 微任务:Promise,async/await
注意!微任务的执行时机比宏任务更早
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| console.log('1');
setTimeout(() => { console.log('2'); });
Promise.resolve().then(() => { console.log('3'); });
console.log('4');
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| console.log('1');
setTimeout(function () { console.log('2'); }, 0);
new Promise(resolve => { console.log('3'); resolve(); console.log('4'); }).then(function () { console.log('5'); }).then(function () { console.log('6'); });
console.log('7');
|
Event Loop 和 DOM 渲染
- 每当 Call Stack 清空时,都是 DOM 重新渲染的机会
- DOM 结构如有改变,则先进行 DOM 渲染,再接着触发下一次 Event Loop
注意!DOM 渲染由 GUI 线程处理,JS 执行由 JS 引擎线程处理,两个线程互斥,不可以同步执行
宏任务和微任务的区别
- 微任务在 DOM 渲染前触发,宏任务在 DOM 渲染后触发
- 因此,微任务的执行时机比宏任务更早
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| const container = document.getElementById('container'); const p1 = document.createElement('p'); const p2 = document.createElement('p'); const p3 = document.createElement('p'); p1.innerHTML = '一段文字 1'; p2.innerHTML = '一段文字 2'; p3.innerHTML = '一段文字 3'; container.append(p1); container.append(p2); container.append(p3);
setTimeout(() => { console.log(container.children.length); alert('宏任务 setTimeout 在 DOM 渲染后触发'); })
Promise.resolve().then(() => { console.log(container.children.length); alert('微任务 Promise 在 DOM 渲染前触发'); })
|
- 微任务是 ES6 规定的,宏任务是浏览器规定的
- 在 Event Loop 中,微任务放入
Micro Task Queue 中(而不是 Callback Queue),不会经过 Web APIs
- 执行顺序:同步任务 > 微任务 > DOM 渲染 > 宏任务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| async function async1() { console.log('async 1 start'); await async2(); console.log('async 1 end'); }
async function async2() { console.log('async 2'); }
console.log('script start');
setTimeout(function () { console.log('setTimeout'); }, 0)
async1();
new Promise(function (resolve) { console.log('promise 1 start'); resolve(); console.log("promise 1 end"); }).then(function () { console.log('promise 2'); })
console.log('script end');
|
手写 Promise
Promise 构造函数与链式调用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
| class MyPromise { state = 'pending'; value = undefined; reason = undefined;
resolveCallbacks = []; rejectCallbacks = [];
constructor(fn) { const resolveHandler = (value) => { if (this.state === 'pending') { this.state = 'fulfilled'; this.value = value; this.resolveCallbacks.forEach(fn => fn(this.value)); } }
const rejectHandler = (reason) => { if (this.state === 'pending') { this.state = 'rejected'; this.reason = reason; this.rejectCallbacks.forEach(fn => fn(this.reason)); } }
try { fn(resolveHandler, rejectHandler); } catch (err) { rejectHandler(err); } }
then(fn1, fn2) { fn1 = typeof fn1 === 'function' ? fn1 : (v) => v; fn2 = typeof fn2 === 'function' ? fn2 : (e) => e;
if (this.state === 'pending') { return new MyPromise((resolve, reject) => { this.resolveCallbacks.push(() => { try { const newValue = fn1(this.value); resolve(newValue); } catch (err) { reject(err); } });
this.rejectCallbacks.push(() => { try { const newReason = fn2(this.reason); reject(newReason); } catch (err) { reject(err); } });
}) }
if (this.state === 'fulfilled') { return new MyPromise((resolve, reject) => { try { const newValue = fn1(this.value); resolve(newValue); } catch (err) { reject(err); } }) }
if (this.state === 'rejected') { return new MyPromise((resolve, reject) => { try { const newReason = fn2(this.reason); reject(newReason); } catch (err) { reject(err); } }) } }
catch(fn) { return this.then(null, fn); } }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| const p1 = new MyPromise((resolve, reject) => {
reject('错误信息……');
})
console.log('p1: ', p1);
const p11 = p1.then(data1 => { console.log('data1: ', data1); return data1 + 1; })
const p12 = p11.then(data2 => { console.log('data2: ', data2); return data2 + 2; })
const p13 = p12.catch(err => { console.error(err); })
|
Promise 方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| const p1 = new MyPromise((resolve, reject) => {
setTimeout(() => { resolve(100); }, 500); }) const p2 = MyPromise.resolve(200); const p3 = MyPromise.resolve(300); const p4 = MyPromise.reject('错误信息');
const p5 = MyPromise.all([p1, p2, p3]); p5.then(result => console.log('all result: ', result));
const p6 = MyPromise.race([p1, p2, p3]); p6.then(result => console.log('race result: ', result));
|
Promise.resolve()
1 2 3
| MyPromise.resolve = function (value) { return new MyPromise((resolve, reject) => resolve(value)); }
|
Promise.reject()
1 2 3
| MyPromise.reject = function (reason) { return new MyPromise((resolve, reject) => reject(reason)); }
|
Promise.all()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| MyPromise.all = function (promiseList = []) { return new MyPromise((resolve, reject) => { const result = []; const length = promiseList.length; let resolvedCount = 0;
promiseList.forEach(p => { p.then(data => { result.push(data); resolvedCount++;
if (resolvedCount === length) { resolve(result); } }).catch(err => { reject(err); }) }) }) }
|
Promise.race()
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| MyPromise.race = function (promiseList = []) { let resolved = false; return new MyPromise((resolve, reject) => { promiseList.forEach(p => { p.then(data => { if (!resolved) { resolve(data); resolved = true; } }).catch(err => { reject(err); }) }) }) }
|