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

异步

异步调用时,调用方不等被调方返回结果就转身离去,因此必须有一种机制让被调方有了结果时能通知调用方。在同一进程中有很多手段可以利用,常用的手段是回调、event 对象和消息
必要时甚至可以把异步函数转换为同步函数。方法很简单:调用异步函数后马上调用 wait 函数等在那里,待异步函数返回结果后再继续往下走。

多线程

有了多线程的支持,可以采用异步调用,调用方和被调方可以属于两个不同的线程,调用方启动被调方线程后,不等对方返回结果就继续执行后续代码。被调方执行完毕后,通过某种手段通知调用方:结果已经出来,请酌情处理
参考:https://www.cnblogs.com/balingybj/p/4780442.html

回调函数实现异步:

 document.onclick = function () {
     console.log('此处异步');
 };
document.addEventListener('click',
   () => {
       console.log('此处异步');
 },   false);
 console.log('此处同步');

解决回调地狱问题

回调地狱

callback hell
层层嵌套的函数,是纵向若业务需求改变不易修改函数的顺序

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

        #box {
            width: 100px;
            height: 100px;
            background-color: blue;
            transition: all .5s;
        }
</style>
<body>
    <div id="box" class="box"></div>
    <script>
        // 三个参数:
        // obj:运动的元素
        // json对象格式:元素的属性和值 
        // endFn: 回调函数(可省)
        const move = (el, { x = 0, y = 0 } = {}, 
         end = () => { }) => {
            el.style.transform = `translate3d(${x}px,${y}px,0)`;
            el.addEventListener(
                'transitionend',
                () => {
                    console.log('end');
                    end();
                },
                false
            );
        };
        const boxEl = document.getElementById('box');
        document.addEventListener('click',
            () => {
                move(boxEl, { x: 100 }, () => {
                    move(boxEl, { x: 100, y: 100 }, () => {
                        move(boxEl, { y: 100 }, () => {
                            move(boxEl, {}, () => {
                            });
                        });
                    });
                });
            },
            false);
    </script>
</body>

用法

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

其中resolve必需,reject可选

状态

有3种
1)完成实例化,默认状态(初始化状态)为pending
2)resolve()执行,状态变为fulfilled/resolved
3)reject()执行,状态变为rejected,并报错

状态只变一次

const p = new Promise((resolve, reject) => {
   reject();
   resolve();
});
console.log(p);//Promise {<rejected>: undefined}

const p = new Promise((resolve, reject) => {
   resolve();
   reject();
});
console.log(p);//Promise {<fulfilled>: undefined}

实例方法

then()

1.根据状态变化,执行两个回调
具体是:
pending->fulfilled,执行第一个回调函数
pending->rejected,执行第二个回调函数,并报错
2.返回Promise对象

const p = new Promise((resolve, reject) => {
    // reject();
    resolve();
});
const p2 = p
    .then(
        () => { },
        () => { }
    );
console.log(p, p2, p2 === p);
//Promise {<fulfilled>: undefined} Promise {<pending>} false

返回的Promise对象状态可改变
1)默认执行resolve(), 返回成功状态的Promise对象

return undefined;

等价于

return new Promise(resolve => {
    resolve(undefined);
});
return 123;

等价于

return new Promise(resolve => {
    resolve(123);
});

2)自定义执行reject(), 返回失败状态的Promise对象

return reason;

等价于

return new Promise(resolve => {
    reject('reason');
});

3)抛出错误,效果相当

向后传值

const p = new Promise((resolve, reject) => {
    // reject();
    resolve();
});
p.then(
    () => {
        console.log('success');
        return undefined;

    },
    () => {
        console.log('err');
        return undefined;
    }
).then(
    data => {
        console.log('success2', data);
    },
    () => {
        console.log('err2');
    }
);

分析:p执行resolve();
1)第一个then()执行第一个回调函数
动作1:打印success;
动作2返回一个成功状态的Promise对象
2)第二个then(),接收了返回值,执行第一个回调函数
3)若想返回一个失败状态的Promise对象,需重新自定义返回语句;第二个then(),接收了返回值,执行第二个回调函数

解决回调地狱

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

        #box {
            width: 100px;
            height: 100px;
            background-color: blue;
            transition: all .5s;
        }
    </style>
</head>

<body>
    <div id="box" class="box"></div>
    <script>
        // 运动函数
        const move = (el, { x = 0, y = 0 } = {}, end = () => { }) => {
            // 模板字符串
            el.style.transform = `translate3d(${x}px,${y}px,0)`;
            el.addEventListener(
                'transitionend',
                () => {
                    console.log('end');
                    end();
                },
                false
            );
        };
        // 改造move函数
        // point为坐标参数
        // reject参数可省
        const movePromise = (el, point) => {
            return new Promise(resolve => {
                move(el, point, () => {
                    resolve();
                });
            })
        };
        const boxEl = document.getElementById('box');
        // 运用then()实现纵向嵌套变横向传递
        document.addEventListener('click',
            () => {
                movePromise(boxEl, { x: 100 })
                    .then(() => {
                        return movePromise(boxEl, { x: 100, y: 100 });
                    }).then(() => {
                        return movePromise(boxEl, { y: 100 });
                    }).then(() => {
                        return movePromise(boxEl, {});
                    });
            },
            false);
    </script>

catch()-状态为rejected时执行

then只写一个回调函数,专门处理resolved状态

then(
        data => { },
        err => { }
    );
    then(
        data => { }
    );

是then()特例

相当于

then(
      null,
      err => { }
  );

用来捕获错误,之后错误消失
一般地,Promise对象后紧跟catch(),先处理Promise内部错误

new Promise((resolve, reject) => {
        reject('reason');
    })
        .catch(err => {
            console.log(err);//reason
            // 默认返回值
            // return undefined;
        })
        .then(
            data => {
                console.log(data);//undefined
            },
        );

连环捕获
多个catch(),前一个catch()有错误

new Promise((resolve, reject) => {
       reject('reason');
   })
       .catch(err => {
           console.log(err);//reason
           // 与自定义失败状态的Promise对象的效果相当
           throw new Error('reason');
       })
       .catch(err => {
           console.log(err);//Error: reason
       });

finally()-状态发生变化时就执行

不接收返回值

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

应用举例:操作数据库成功失败与否,之后都需关闭数据库

是then()特例

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

构造函数方法

Promise.resolve()-成功状态Promise的简写

new Promise(resolve => resolve('foo'));

简写为

Promise.resolve('foo');

返回值:Promise原对象,不做其他
例:
1.参数-一般参数
Promise.resolve(‘foo’)
.then(data => {
console.log(data);//foo
});
2.参数-Promise对象

const p1 = new Promise(resolve => {
    setTimeout(resolve, 1000, '我执行了');
    // 相当于
    // setTimeout(() => {
    //     console.log('我执行了');
    // }, 1000);
});
Promise.resolve(p1)
    .then(data => {
        console.log(data);//我执行了(2nd)
    });
// 相当于
// p1.then(data => {
//     console.log(data);
// });
console.log(Promise.resolve(p1) === p1);//true(1st)

简写还原
resolve()函数接收Promise对象时,then根据传递的Promise状态变化决定执行哪个回调

const p1 = new Promise(resolve => {
    setTimeout(resolve, 1000, '我执行了');
});
new Promise(resolve => resolve(p1)).then(data => {
    console.log(data);
});

3.参数-具有then()方法的对象
思考:如何实现Promise对象调用then()一样的效果
问题:不执行then()

const thenable = {
       then() {
           console.log('then');
           // 不可用return语句设置返回值
           // then()不在Promise对象中
           //return语句无法被包装为Promise对象
           // return 123;
       }
   };
   // 调用了thenable的then()方法
   // 输出then
   // 仍处于pending状态,故不执行then()
   Promise.resolve(thenable)
       .then(
           data => console.log(data),
           err => console.log(err)
       );

解决:

const thenable = {
     then(resolve, reject) {
         // resolve('then');
         reject('reason');
     }
 };
 Promise.resolve(thenable)
     .then(
         data => console.log(data),
         err => console.log(err)
     );

Promise.reject ()-失败状态Promise的简写

new Promise(resolve,reject => reject ('foo'));

简写为

Promise. reject ('foo');

参数-原封不动向后传递
1.Promise对象
不执行其中的语句

   const p1 = new Promise(resolve => {
        setTimeout(resolve, 1000, '我执行了');
    });``
    Promise.reject(p1)
        .catch(err => console.log(err) //Promise {<pending>}
        );

2.一般参数
1)简化还原形式

new Promise((resolve, reject) => {
        resolve(123);
    })
        .then(data => {
            // 默认返回成功状态Promise
            return data;
        })
        .then(data => {
            console.log(data);//123
        });

2)传一般参数

new Promise((resolve, reject) => {
    resolve(123);
})
    .then(data => {
        // 自定义返回失败状态Promise
        return Promise.reject('reason');
    })
    .catch(err => {
        console.log(err);//reason
    });

Promise.all()-取决于所有传入的Promise状态

返回值:新的Promise
所有Promise状态为resolved,其状态才为resolved
存在一个Promise状态为rejected,其状态就为rejected
分析:
1)第1s, p1返回成功对象,第2s,p2返回成功对象,同时p执行第一个回调函数,打印[‘p1’, ‘p2’]
2) 第1s, p1返回成功对象,第2s,p2返回失败对象,同时p执行第二个回调函数,打印’p2’
return Promise.reject(‘p2’);
3)第1s, p1返回失败对象,同时p执行第二个回调函数,打印’p1’ ;第2s,p2返回成功对象

 return Promise.reject('p1');
// 延迟函数
const delay = ms => {
    return new Promise(
        resolve => {
            // 执行resolve()
            // 返回成功状态的对象1
            setTimeout(resolve, ms);
        }
    );
};
const p1 = delay(1000).then(() => {
    console.log('p1完成了');
    // 返回成功状态的对象2.1
    return 'p1';
});
const p2 = delay(2000).then(() => {
    console.log('p2完成了');
    // 返回成功状态的对象2.2
    return 'p2';
});
// 返回成功状态的对象3
const p = Promise.all([p1, p2]);
p.then(
    data => { console.log(data); },
    err => { console.log(err); }
);

Promise.race()-取决于第一个传入的Promise状态

Promise.allSettled()-状态恒为成功

与传入的Promise状态无关
记录传入的各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); });

错误可单独,也可统一处理
1)单独

const delay = ms => {
    return new Promise(
        resolve => {
            setTimeout(resolve, ms);
        }
    );
};
const p1 = delay(1000).then(() => {
    return 'p1';
    return Promise.reject('p1');
}).catch(err => {
    console.log('p1', err);
});
const p2 = delay(2000).then(() => {
    return 'p2';
    return Promise.reject('p2');
}).catch(err => {
    console.log('p2', err);
});
const p = Promise.allSettled([p1, p2]);
p.then(
    data => { console.log(data); },
    err => { console.log(err); }
);

2)统一

const delay = ms => {
    return new Promise(
        resolve => {
            setTimeout(resolve, ms);
        }
    );
};
const p1 = delay(1000).then(() => {
    return 'p1';
    return Promise.reject('p1');
});
const p2 = delay(2000).then(() => {
    return 'p2';
    return Promise.reject('p2');
});
const p = Promise.allSettled([p1, p2]);
p.catch(err => {
    console.log(err)
})
    .then(
        data => { console.log(data); },
        err => { console.log(err); }
    );

应用-异步加载图片

<img
  src="https://img3.mukewang.com/61df86410001bf0517920764.jpg"
  alt=""
  id="img"
/>
<script>
  const loadImgAsync = (url) => {
    return new Promise((resolve, reject) => {
      const img = new Image();
      img.onload = () => {
        resolve(img);
      };
      img.onerror = () => {
        reject(new Error(`Couldn't load image at ${url}`));
      };
      img.src = url;
    });
  };
  const imgDOM = document.getElementById("img");
  loadImgAsync("https://img2.mukewang.com/61df86770001052f17920764.jpg")
    .then((img) => {
      //   console.log(img);
      //   console.log(img.src);
      imgDOM.src = img.src;
    })
    .catch((err) => {
      console.log(err);
    });
</script>
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值