ES6 Promise

185 阅读9分钟

异步和单线程

同步和异步的区别是什么?

前端使用异步的场景有哪些?

知识点

JS是单线程语言,只能同时做一件事儿

浏览器和nodejs 已支持JS启动进程,如Web Worker

JS和DOM渲染共用同一个线程,因为JS可修改DOM结构

遇到等待(网络请求,定时任务)不能卡住

需要异步

回调callback函数形式

DdRdX9.png

DdRR6H.png

基于JS是单线程语言

异步不会阻塞代码执行

同步会阻塞代码执行

应用场景

网络请求,如ajax图片加载

定时任务,如setTimeout

DdWVBR.png

DdWcEq.png

DdWq56.png

DdWvxe.png

setTimeout 笔试题

console.log(1)
setTimeout(function(){
    console.log(2)
} ,1000)
console.log(3)
setTimeout(function () {
    console.log(4)
},0)
console.log(5)

全文简介

学什么

初识 Promise

Promise的实例方法

Promise的构造函数方法

Promise的注意事项和应用

初识Promise

Promise是什么

Promise的基本用法

Promise的实例方法

then()

catch()

finally()

Promise的构造函数方法

Promise.resolve()

Promise.reject()

Promise.all()

Promise.race()

Promise.allSettled()

Promise的注意事项和应用

Promise的注意事项

Promise的应用

Promise

1.Promise是什么

认识Promise

什么时候使用Promise

1.认识 Promise

Promise 是异步操作的一种解决方案

//回调函数解决异步问题
document.onclick = function(){
    console.log('这里是异步的');
}

console.log('这里是同步的');

2.什么时候使用 Promise

Promise 一般用来解决层层嵌套的回调函数(回调地狱 callback hell)的问题

[DdfKZn.png](imgchr.com/i/DdfKZn)

image-20201125233156332

DdIC6S.png

<style>
    * {
        padding: 0;
        margin: 0;
    }

    #box {
        width: 300px;
        height: 300px;
        background-color: orange;
        transition: all 0.5s;
    }
</style>
</head>
<body>
    <div id="box"></div>
    <script>
        const move = (el, { x = 0, y = 0 } = {}, end = () => {}) => {
            el.style.transform = `translate3d(${x}px, ${y}px, 0)`;

            el.ontransitionend = function () {
                end();
            };
        };

        const oBox = document.getElementById("box");

        document.onclick = function(){
            move(oBox,{x: 150, y: 0},() => {
                move(oBox,{x: 150, y: 150},() => {
                    move(oBox, { x: 0, y: 150 },() => {
                        move(oBox, { x: 0, y: 0 });
                    })
                })
            })
        }
    </script>

2.Promise 的基本用法

实例化构造函数生成实例对象

Promise的状态

then方法

resolve和reject 函数的参数

1.实例化构造函数生成实例对象

console.log(Promise); //构造函数

// Promise 解决的不是回调函数,而是回调地狱
const p = new Promise(() => {});

2.Promise 的状态

Promise 有 3 种状态,一开始是 pending(未完成)

执行 resolve,变成 fulfilled(resolved),已成功

执行 reject,变成 rejected,已失败

Promise 的状态一旦变化,就不会再改变了

const p = new Promise((resolve, reject) => {
    // pending-> fulfilled   或者 resolved
    // resolve(); //变为成功态


    //pending -> rejected //变为失败态
    //reject(); 
}

console.log(p);

3.then 方法

p.then(
    () => {
        console.log('success');
    },
    () => {
        consol.log('error');
    } 
    //成功执行第一个  失败执行第二个
);

4.resolve 和 reject 函数的参数

const p = new Promise((resolve, reject) => {
    resolve({ name: "云牧", age: 18 });

    //reject('reason');
    //一般失败传递一个错误对象
    reject(new Error('reason'));
});


p.then(
    (data) => {
        console.log("我是成功的", data);
    },
    () => {
        console.log("我是失败的");
    }
);

3.实例方法

then()

  • 什么时候执行

  • 执行后的返回值

  • then方法返回的Promise 对象的状态改变

  • 向后传值

  • 使用Promise解决回调地狱

1.什么时候执行

pending -> fulfilled 时,执行 then 的第一个回调函数

pending -> rejected 时,执行 then 的第二个回调函数

2.执行后的返回值

then 方法执行后返回一个新的 Promise 对象

const p1 = new Promise((resolve, reject) => {
    resolve();
    // reject();
});


const p2 = p1.then(
    () => {},
    () => {}
)//.then().then();

console.log(p1);
console.log(p2); //p2也返回一个promise对象
console.log(p1 === p2);

3.then 方法返回的 Promise 对象的状态改变

const p = new Promise((resolve, reject) => {
    //resolve();

    reject();
});

p.then(
    () => {
        console.log("我是成功的1");
    },
    () => {
        console.log("我是失败的1");
        //在 then 的回调函数中,return 后面的东西,会用 Promise 包装一下
        //return undefined;
        //等价于
        //  return new Promise(resolve => {
        //   resolve(undefined);   默认返回的永远都是成功状态的 Promise 对象
        // });


        //return 123;
        //等价于
        // return new Promise(resolve => {
        //   resolve(123);
        // });

        //要返回失败的Promise对象的话
        return new Promise((resolve, reject) => {
            reject("reson");
        });
        //或者
        throw new Error("reson2");
    }
).then(
    () => {
        console.log("我是成功的2");
    },
    (err) => {
        console.log("我是失败的2", err);
    }
);

改写之前的运动

<body>
    <style>
        * {
            padding: 0;
            margin: 0;
        }

        #box {
            width: 300px;
            height: 300px;
            background-color: orange;
            transition: all 0.5s;
        }
    </style>
    </head>
<body>
    <div id="box"></div>
    <script>
        const move = (el, { x = 0, y = 0 } = {}, end = () => {}) => {
            el.style.transform = `translate3d(${x}px, ${y}px, 0)`;

            el.ontransitionend = function () {
                end();
            };
        };

        const oBox = document.getElementById("box");

        const moveEl = (el, point) => {
            return new Promise((reslove) => {
                move(el, point, () => {
                    reslove();
                });
            });
        }; 



        document.onclick = function(){
            moveEl(oBox,{x: 150, y: 0}).then(() => {
                return moveEl( oBox,{x: 150, y: 150} )
            }).then(() => {
                return moveEl( oBox,{x: 0, y: 150} )
            }).then(() => {
                return moveEl( oBox,{x: 0, y: 0} )
            })
        }
    </script>
</body>

catch()

有什么用

基本用法

1.有什么用

then(
    data => {},
    err => {}
);//then方法接收两个函数 第一个处理成功函数  第二个处理失败的函数

then(data => {}); //但是我们一般只需要成功的函数  

catch 专门用来处理 rejected 状态

then(null, err => {});

举例

new Promise((resolve, reject) => {
    // resolve(123);
    reject('reason');
})
.then(data => {
    console.log(data);
})
// .then(null, err => {
//   console.log(err);
// });
    .catch(err => {
    console.log(err);

    // return undefined;
    throw new Error('reason');
}).then(data => {
    console.log(data);
})
.catch(err => {
    console.log(err);
});

//catch() 可以捕获它前面的错误
// 一般总是建议,Promise 对象后面要跟 catch 方法,这样可以处理 Promise 内部发生的错误

finally()

什么时候执行

本质

1.什么时候执行

当 Promise 状态发生变化时,不论如何变化都会执行,不变化不执行

new Promise((resolve, reject) => {
    // resolve(123);
    reject('reason');
})
    .finally(data => {
    console.log(data);
})
    .catch(err => {});

//当我们使用javascript去操作数据库  数据库很多操作也可以使用promise
//很多时候不管成功还是失败都需要关闭数据库 都需要用到finally

2.本质

finally() 本质上是 then() 的特例

new Promise((resolve, reject) => {
    //   // resolve(123);
    //   reject('reason');
    // })
    //   .finally(data => {
    //     console.log(data);
    //   })
    //   .catch(err => {});
    
    
    // 等同于
    new Promise((resolve, reject) => {
        // resolve(123);
        reject('reason');
    })
        .then(
        result => {
            return result;
        },
        err => {
            return new Promise((resolve, reject) => {
                reject(err);
            });
        }
    )
   .then(data => {
        console.log(data);
    })
        .catch(err => {
        console.log(err);
    });

Promise.resolve()和Promise.reject()

本质

参数

在then方法中的应用

1.Promise.resolve()

是成功状态 Promise 的一种简写形式

new Promise(resolve => resolve('foo'));
// 简写
Promise.resolve('foo');
参数

一般参数

Promise.resolve('foo').then(data => {
    console.log(data);
});

当 Promise.resolve() 接收的是 Promise 对象时,直接返回这个 Promise 对象,什么都不做




const p1 = new Promise(resolve => {
    setTimeout(resolve, 1000, '我执行了');
    // setTimeout(() => {
    //   resolve('我执行了');
    // }, 1000); 
});


Promise.resolve(p1).then(data => {
    console.log(data);
});

// 等价于
//p1.then(data => {
//console.log(data);
//});
//console.log(Promise.resolve(p1) === p1);

console.log(Promise.resolve(p) === p); //true



// 当 resolve 函数接收的是 Promise 对象时,后面的 then 会根据传递的 Promise 对象的状态变化决定执行哪一个回调
new Promise(resolve => resolve(p1)).then(data => {
    console.log(data);
});

Promise.all()

  • 有什么用
  • 基本用法

1.有什么用

Promise.all() 关注多个 Promise 对象的状态变化

传入多个 Promise 实例,包装成一个新的 Promise 实例返回

2.基本用法

Promise.all() 的状态变化与所有传入的 Promise 实例对象状态有关

所有状态都变成 resolved,最终的状态才会变成 resolved

只要有一个变成 rejected,最终的状态就变成 rejected

const delay = ms => {
    return new Promise(resolve => {
        setTimeout(resolve, ms);
    });
};

const p1 = delay(1000).then(() => {
    console.log('p1 完成了');

    return "p1的数据";
    //return Promise.reject('reason');
});

const p2 = delay(2000).then(() => {
    console.log('p2 完成了');

    return "p2的数据";
    // return Promise.reject('reason');
});

Promise.all() 的状态变化与所有传入的 Promise 实例对象状态有关

//所有状态都变成 resolved,最终的状态才会变成 resolved
//只要有一个变成 rejected,最终的状态就变成 rejected
const p = Promise.all([p1, p2]);
p.then(
    data => {
        console.log(data);
    },
    err => {
        console.log(err);
    }
);

Promise.any()

只要其中的一个 promise 成功,就返回那个已经成功的 promise

所有的 promises 都失败/拒绝),就返回一个失败的 promise

const promises = [
  Promise.reject('ERROR A'),
  Promise.reject('ERROR B'),
  Promise.resolve('result'),
]

Promise.any(promises).then((value) => {
  console.log('value: ', value)
}).catch((err) => {
  console.log('err: ', err)
})

// value:  result

如果所有传入的 promises 都失败:

const promises = [
  Promise.reject('ERROR A'),
  Promise.reject('ERROR B'),
  Promise.reject('ERROR C'),
]

Promise.any(promises).then((value) => {
  console.log('value:', value)
}).catch((err) => {
  console.log('err:', err)
  console.log(err.message)
  console.log(err.name)
  console.log(err.errors)
})

// err: AggregateError: All promises were rejected
// All promises were rejected
// AggregateError
// ["ERROR A", "ERROR B", "ERROR C"]

Promise.race() 和 Promise.allSettled()

1.Promise.race()

const delay = ms => {
    return new Promise(resolve => {
        setTimeout(resolve, ms);
    });
};
const p1 = delay(1000).then(() => {
    console.log('p1 完成了');

    return 'p1';
    // return Promise.reject('reason');
});
const p2 = delay(2000).then(() => {
    console.log('p2 完成了');

    // return 'p2';
    return Promise.reject('reason');
});

//romise.race() 的状态取决于第一个完成的 Promise 实例对象,如果第一个完成的成功了,那最终的就成功;如果第一个完成的失败了,那最终的就失败
const racePromise = Promise.race([p1, p2]);
racePromise.then(
    data => {
        console.log(data);
    },
    err => {
        console.log(err);
    }
);

2.Promise.allSettled()

//Promise.allSettled() 的状态与传入的Promise 状态无关
//永远都是成功的
//它只会忠实的记录下各个 Promise 的表现
const allSettledPromise = Promise.allSettled([p1, p2]);
allSettledPromise.then(data => {
    console.log('succ', data);
});

4.Promise的注意事项

  • resolve或reject 执行后的代码
  • Promise.all/race/allSettled的参数问题
  • Promise.all/race/allSettled 的错误处理

1.resolve 或 reject 函数执行后的代码

推荐在调用 resolve 或 reject 函数的时候加上 return,不再执行它们后面的代码

new Promise((resolve, reject) => {
    //resolve(123);
    // return resolve(123);
    //  return reject('reason');

    console.log('hi');
});

2.Promise.all/race/allSettled 的参数问题

参数如果不是 Promise 数组,会将不是 Promise 的数组元素转变成 Promise 对象

Promise.all([1, 2, 3]).then(datas => {
    console.log(datas);
});

//等价于
Promise.all([
    Promise.resolve(1),
    Promise.resolve(2),
    Promise.resolve(3)
]).then(datas => {
    console.log(datas);
});



//不只是数组,任何可遍历的都可以作为参数
//数组、字符串、Set、Map、NodeList、arguments
Promise.all(new Set([1, 2, 3])).then(datas => {
    console.log(datas);
});

3.Promise.all/race/allSettled 的错误处理

错误既可以单独处理,也可以统一处理

一旦被处理,就不会在其他地方再处理一遍

const delay = ms => {
    return new Promise(resolve => {
        setTimeout(resolve, ms);
    });
};

const p1 = delay(1000).then(() => {
    console.log('p1 完成了');

    // return 'p1';
    return Promise.reject('reason');
});
// .catch(err => {
//   console.log('p1', err);
// });
const p2 = delay(2000).then(() => {
    console.log('p2 完成了');

    return 'p2';
    // return Promise.reject('reason');
});
// // .catch(err => {
// //   console.log('p2', err);
// });

const allPromise = Promise.all([p1, p2]);
allPromise
    .then(datas => {
    console.log(datas);
})
    .catch(err => console.log(err));
// 第一题
Promise.resolve().then(() => {
    console.log(1)
}).catch(() => {
    console.log(2)
}).then(() => {
    console.log(3)
})

// 第二题
Promise.resolve().then(() => { // 返回 rejected 状态的 promise
    console.log(1)
    throw new Error('erro1')
}).catch(() => { // 返回 resolved 状态的 promise
    console.log(2)
}).then(() => {
    console.log(3)
})

// 第三题
Promise.resolve().then(() => { // 返回 rejected 状态的 promise
    console.log(1)
    throw new Error('erro1')
}).catch(() => { // 返回 resolved 状态的 promise
    console.log(2)
}).catch(() => {
    console.log(3)
})

5.Promise的应用

const loadImg = url => {
    return new Promise((resolve,reject) => {

        let newImg = document.createElement("img");

        newImg.onload = function(){
            resolve(newImg);
        }


        newImg.onerror = function(){
            reject(new Error(`Could not load lmage at ${url}`));
        }


        newImg.src = url;

    })
}

loadImg("https://s1.ax1x.com/2020/09/26/0irQ0g.jpg").then( img => {


    console.log(img.width,img.height);

    document.body.appendChild(img);

    return loadImg("https://s1.ax1x.com/2020/09/26/0irm1P.jpg");

}).then(img => {

    console.log(img.width,img.height);

    document.body.appendChild(img)
}).catch(err => {console.log(err)})

全文总结

Promise

初识Promise

Promise的实例方法

Promise的构造函数方法

Promise的注意事项和应用

Promise是什么

Promise是异步操作的一种解决方案

Promise一般用来解决层层嵌套的回调函数的问题

Promise解决的不是回调函数,而是回调地狱

Promise的状态

Promise有3种状态

pending(未完成)

resolved/fulfilled(已成功)

rejected(已失败)

状态一旦变化,就不会再改变了

Promise的基本用法

D0z7dI.png

推荐在调用resolve或 reject函数的时候加上return

then()

pending->resolved时,执行then的第一个回调函数

pending->rejected时,执行then的第二个回调函数

状态不改变,then()的回调函数都不会被执行

then()执行后返回一个新的Promise对象

可以通过return语句改变返回的Promise对象的状态

then()可以向后传值

DBSIpT.png

catch()

catch专门用来处理rejected状态

catch 本质上是then的特例

建议Promise 对象后面要跟catch方法,这样可以处理Promise内部发生的错误

finally()

当Promise状态发生变化时,不论如何变化都会执行

finally()本质上是then()的特例

Promise.resolve()的本质

成功状态Promise的一种简写形式

DBSvh6.png

Promise.resolve()的参数

参数是 Promise 实例对象时,直接返回这个Promise对象

参数是其他值时,相当于通过resolve函数传参

Promise.reject()

失败状态 Promise的一种简写形式

不管什么参数,都会原封不动地向后传递,作为后续方法的参数

DBpAAI.png

Promise.all/race/allSettled()

只要是可遍历的,都可作为参数

参数的“集合”中若有成员不是 Promise 对象,内部会将其转变成Promise 对象

返回一个新的 Promise 实例对象

错误既可以单独处理,也可以统一处理

Promise.all()

所有状态都变成resolved,最终的状态才会变成resolved

只要有一个变成rejected,最终的状态就变成 rejected

Promise.race()

最终的状态取决于第一个完成的 Promise 实例对象

如果第一个完成的成功了,那最终的就成功

Promise.allSettled()

最终的状态永远都是成功的,与传入的Promise 对象状态无关

会记录下各个Promise的表现