首页 > 其他分享 >Promise的理解和使用

Promise的理解和使用

时间:2022-12-10 13:44:48浏览次数:39  
标签:resolve console log value reason 理解 Promise 使用

1.1 实例对象和函数对象

  • 实例对象:new 函数产生对象,称为实例对象简称对象。
  • 函数对象:将函数作为对象使用时,称为函数对象。
function Func() { // Func只能称为函数

}

const func = new Func(); // Fn只能new过的才可以称为构造函数
// func称为实例对象
console.log(func.prototype); // 函数对象

1.2 两种典型的回调函数

1. 同步回调

立即执行,完全执行完了才会结束,不会放入回调队列中

const arr = [1, 3, 5];
arr.forEach(item => { // 遍历回调,同步回调,不会放入队列中一上来就会执行
  console.log(item);
});
console.log("forEach()执行完毕之后");

2. 异步回调

不会立即执行,会放入回调队列中将来执行
eg: setTimeout、setInterval、setImmediate、Ajax请求、Script脚本、Promise函数

// 定时器回调
setTimeout(() => { // 异步回调,会放入队列中将来执行
  console.log("timeout callback()');
}, 0);
console.log("setTimeout()之后");

// Promise成功或失败的回调
new Promise((resolve, reject) => {

}).then(
  value => {console.log("value", value)},
  reason => {console.log("reason", reason)},
)
console.log(".....");

JS引擎先把初始化的同步代码都执行完成之后,才执行回调队列中的代码 (执行时机:同步代码 > 回调队列)

1.3 JavaScript中异常error处理

1. 错误的类型
  • Error: 所有错误的父类型
  • ReferenceError: 引用的变量不存在
console.log(a); // ReferenceError:a
  • TypeError: 数据类型不正确
let b;
console.log(b.xxx);
// TypeError: Cannot read property 'xxx' of undefined

let c = {};
c.xxx();
// TypeError: c.xxx is not a function 
  • RangeError: 数据值不在其所允许的范围内
function fn() {
  fn();
}
fn()

// RangeError: MAximum call stack size exceeded

  • SyntaxError: 语法错误
const c = """";
// SyntaxError: Unexpected string

2. 错误处理(捕获与抛出)

抛出错误:throw error

  function something() {
    if(Date.now() % 2 === 1 ) {
      console.log("当前时间为奇数, 可以执行任务");
    }  else {
      throw new Error("当前时间为偶数,无法执行任务");
    }
  }

捕获错误:try ... catch

try {
  something();
} catch (error) {
  alert(error.message);
}

3. 错误对象

  • message属性:错误相关信息
  • stack属性:函数调用栈记录信息
try {
  let d;
  console.log(d.xxx);
} catch (error) {
  console.log(error.message);  
  console.log(error.stack);
}
console.log("出错之后");
// Cannnot read property "xxx" of undefined
// TypeError: Cannot read property "xxx" of undefined
// 出错之后

因为错误被捕获处理,后面的代码运行下去,打印出错之后

Promise的理解和使用

Promise是什么?

简单来说:Promise是JS中异步编程的解决方案,传统采用回调来实现异步编程,缺点:回调地狱问题。
具体表达:

  • Promise本质上是一个构造函数里面包含all、race、reject、resolve等方法,原型上则包含then、catch、finally等方法。
  • Promise功能上是用来封装一个异步操作并获取其成功/失败的值。

Promise的状态?

实例对象Promise中的一个属性PromiseState
pending、resolved/fulfilled、rejected
状态变换有两种方式:

  1. pending => fulfiled(已完成)
  2. pending => rejected(已失败)

注意项:

  1. 外界的状态不受外界影响。
  2. 只有这两种,且一个promise对象只能改变一次。
  3. 一旦状态改变,就不会再变,任何时候都可以得到这个结果。
  4. 无论成功还是失败,都会有一个结果数据。成功的结果称之为【value】 失败的结果称之为【reason】

Promise对象的值

实例对象Promise的另一个值是PromiseResult,保存对象成功/失败的值(value/reason)

Promise的基本流程

const promise = new Promise(function(resolve, reject){
    // ... some code
    if(异步操作成功) {
      resolve(value);
    } else {
      reject(reason);
    }
});

Promise构造函数接收一个函数(执行器函数)作为参数,该函数的两个参数分别是resolve和reject。
他们是两个函数,有JS引擎提供,不用自己部署。

resolve函数作用:
将Promise对象的状态从【未完成】变为【成功】即从pending变为resolved
在异步操作成功的时候调用,并将异步操作的结果作为参数value传递出去。

reject函数作用:
将Promise对象的状态从【未完成】变为【失败】即从pending变为rejected
在异步操作失败的时候调用,并将异步操作报出的错位,作为参数error/reason传递出去。

Promise实例生成以后,可以用then方法分别指定resolved状态和rejected状态的回调函数。

promise.then(function(value) {
  // success
}, function(reason) {

});

then方法可以接收两个回调函数作为参数。
第一个回调函数onResolved()是Promise对象的状态变为resolved的时候调用。
第二个回调函数onRejected()是Promise对象的状态变为rejected的时候调用。
两个函数可选不一定要提供,都接收Promise对象传出的值作为参数。

    <script>
        // 创建一个新的p对象promise
        // 执行器函数
        const p = new Promise((resolve, reject) => {
            // 执行异步操作任务
            setTimeout(() => {
                const time = Date.now();
                // 如果当前时间是偶数代表成功,否则失败
                if (time % 2 === 0) {
                    // 如果成功调用resolve(value)
                    resolve("成功的数据, time = " + time);
                } else {
                    resolve("失败的数据, time = " + time);
                }
            }, 1000);
        })

        p.then((value) => {
            // 接收得到成功的value数据 onResolved
            console.log("成功回调:", value);
        }, (reason) => {
            // 接收得到失败的reason数据 onRejected
            console.log("失败回调:", reason);
        })
    </script>

.then()和执行器(executor)同步执行,.then()中的回调函数异步执行

2.2 为什么要使用Promise?

1. 指定回调函数的方式更加灵活

旧的:必须在启动异步任务之前指定

// 纯回调的方式
// 成功的回调函数
function successCallback(result) {
  console.log("声音文件创建成功:" + result);
}

// 失败的回调函数
function failureCallback(error) {
  console.log("声音文件创建失败:" + error);
}

// 必须指定回调函数,再执行异步任务
createAudioFileAsync(audioSettings, successCallback, failureCallback); // 回调函数在执行异步任务(函数)之前就需要指定

Promise: 启动异步任务 =》 返回Promise对象 =》 给Promise对象绑定回调函数(甚至可以在异步任务结束之后指定)

// 使用Promise
const promise = createAudioFileAsync(audioSettings); // 执行2秒

setTimeout(() => {
  promise.then(successCallback, failureCallback); // 也可以获取
}, 3000);

2. 支持链式调用,可以解决回调地狱问题

什么是回调地狱?

回调函数嵌套调用,外部回调函数异步执行的结果是其内部嵌套的回调函数执行的条件。

doSomething(function(result) {
  doSomethingElse(result, function(newReuslt){
    doThirdThing(newResult, function(finalResult){
      console.log("Got the final result: "  + finalResult);
    }, failureCallback)
  }, failureCallback)
}, failureCallback)
回调地狱的缺点?
  1. 不便于阅读
  2. 不便于异常处理
解决方法?
  • Promise链式调用

使用Promise的链式调用解决回调地狱问题

doSomething()
  .then(result => doSomethingElse(result))
  .then(newResult => doThirdThing(newResult))
  .then(finalResult => {console.log("Got the final result: " + finalResult)})
  .catch(failureCallback);
终极解决方案?
  • async/await
    回调地狱终极解决方案 async/await
async function request() {
  try {
    const result = await doSomething();
    const newResult = await doSomethingElse(result);
    const finalResult = await doThirdThing(newResult);
    console.log("Got the final result: " + finalResult);
  } catch (error) {
    failureCallback(error);
  }
}

2.3 如何使用Promise

1. Promise构造函数: Promise(executor) {}

  • executor函数:同步执行(resolve, reject) => {}
  • resolve函数:内部定义成功时调用的函数 resolve(value);
  • reject函数:内部定义失败时调用的函数 reject(reason);

说明: executor是执行器,会在Promise内部立即同步回调,异步操作resolve/reject就在executor中执行

2. Promise.prototype.then方法: p.then(onResolved, onRejected) 指定两个回调(成功 + 失败)

  • onResolved函数:成功的回调函数(value) => {}
  • onRejected函数:失败的回调函数(reason) => {}

说明:指定用于得到成功value的成功回调和用于得到失败reason的失败回调,返回一个新的Promise对象。

3. Promise.prototype.catch方法:p.catch(onRejected) 指定失败的回调

  1. onRejected函数:失败的回调函数(reason) => {}
    说明: then()的语法糖,相当于then(undefined, onRejected)
new Promise((resolve, reject) => {
  setTimeout(() => {
    if(...) {
      resolve("成功的数据"); // resolve()函数
    } else {
      reject("失败的数据"); // reject()函数
    }
  }, 1000)
}).then(
    value => {  // onResolved()函数
      console.log(value); // 成功的数据
    }
).catch(
  reason => {  // onRejected()函数
    console.log(reason); // 失败的数据
  }
);

4. Promise.resolve()方法:Promise.resolve(value)

value: 将被Promise对象解析的参数,也可以是一个成功或失败的Promise对象
返回:返回一个带着定值解析过的Promise对象,如果参数本身就是一个Promise对象,则直接返回这个Promise对象。

  1. 如果传入的参数未非Promise类型的对象,则返回的结果为成功Promise对象
let p1 = Promise.resolve(521);
console.log(p1); // Promise {<fulfilled>: 521}
  1. 如果传入的参数为Promise对象,则参数的结果决定了resolve的结果
  let p2 = Promise.resolve(new Promise((resolve, reject) => {
            // 参数的结果决定返回的结果
            // resolve("OK"); // 成功的Promise
            reject("Error"); // 失败的Promise
  }));
  console.log(p2);
  p2.catch(reason => {
      console.log(reason);
  });
  • Promise.resolve()/Promise.reject()方法是一个语法糖用来快速得到Promise对象
// 产生一个成功值为1的Promise对象
new Promise((resolve, reject) => {
    resolve(1);
});

// 相当于
const p1 = Promise.resolve(1);
const p2 = Promise.resolve(2);
const p3 = Promise.resolve(3);

p1.then(value => {console.log(value)}); // 1
p2.then(value => {console.log(value)}); // 2
p3.then(reason => {console.log(reason)}); // 3

Promise.all方法: Promise.all(iterable)

iterable: 包含n个promise的可迭代对象,eg: Array或String
说明: 返回一个新的promise,只有所有的promise都成功才能成功,只要有一个失败了则就直接失败。

let p1 = new Promise((resolve, reject) => {
    resolve("OK");
});

let p2 = Promise.resolve("Success");
let p3 = Promise.resolve("Oh Yeah");

const result = Promise.all([p1, p2, p3]);
console.log(result);
Promise.all()使用案例
const p1 = Promise.resolve(1)
const p2 = Promise.resolve(2)
const p3 = Promise.reject(3)

const pAll = Promise.all([p1, p2, p3])
const pAll2 = Promise.all([p1, p2])
//因为其中p3是失败所以pAll失败
pAll.then(
value => {
   console.log('all onResolved()', value)
 },
reason => {
   console.log('all onRejected()', reason) 
 }
)
// all onRejected() 3
pAll2.then(
values => {
   console.log('all onResolved()', values)
 },
reason => {
   console.log('all onRejected()', reason) 
 }
)
// all onResolved() [1, 2]

7. Promise.race方法: Promise.race(iterable)

iterable: 包含n个promise的可迭代对象,eg:Array或String
DESC:返回一个新的Promise,第一个完成的Promise的结果状态就是最终的结果状态,谁先完成就输出谁(不管是成功还是失败)

const pRace = Promise.race([p1, p2, p3]);
// 谁先完成就输出谁(不管是成功还是失败)
const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(1)
  }, 1000);
});

const p2 = Promise.resolve(2);
const p3 = Promise.reject(3);

pRace.then(
value => {
  console.log('race onResolved()', value);
},
reason => {
  console.log('race onRejected()', reason);
});
// race onResolved() 2
Promise.race()使用案例
let p1 = new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve("OK");
    }, 1000);
});
let p2 = Promise.resolve("Success");
let p3 = Promise.resolve("Oh Yeah");

// 调用
const result = Promise.race([p1, p2, p3]);

console.log(result);

Promise的几个关键问题

1. 如何改变Promise的状态

  1. resolve(value): 如果当前是pending就会变为resolved
  2. reject(reason): 如果当前是pending就会变为rejected
  3. 抛出异常:如果当前是pending就会变为rejected
const p = new Promise((resolve, reject) => {
    // resolve(1);  变为resolved成功状态
    // reject(2); 变为rejected失败状态
    throw new Error("出错了"); // 抛出异常 变为rejected失败状态 reason为抛出的error
})
p.then(
  value => {},
  reason => {console.log('reason', reason)}
)

2. 一个Promise指定多个成功/失败回调函数,都会调用吗?

当Promise改变为对应状态时都会调用

const p = new Promise((resolve, reject) => {
    // resolve(1)
    reject(2)
})

p.then(
  value => {},
  reason => {console.log('reason', reason)}
)
p.then(
  value => {},
  reason => {console.log('reason2', reason)}
)
// reason 2
// reason2 2

3. 改变Promise状态和指定回调函数谁先谁后?

都有可能,常规时先指定回调再改变状态,但也可以先改变状态再指定回调

  • 如何先改变状态再指定回调?
    1)在执行器中直接调用resolve()/reject()
  1. 延迟更长时间才会调用then()
let p = new Promise((resolve, reject) => {
  // setTimeout(() => {
      resolve("OK");
  }, 1000); // 有异步就先指定回调,否则先改变状态
});
p.then(value => {
  console.log(value);
}, reason => {
  console.log(reason);
});
  • 什么时候才能得到数据?

  • 如果先指定的回调,当状态发生改变的时候,回调函数就会得到数据;

new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(1); // 改变状态
  }, 1000);
}).then(
  value => {},
  reason => {}
)
  • 如果先改变的状态,那当指定回调时,回调函数就会调用得到数据;
    ``
    new Promise((resolve, reject) => {
    resolve(1) // 改变状态
    }).then( // 指定回调函数
    value => {},
    reason =>{}
    )

#### Promise如何串联多个操作任务 ?
1) Promise的then()返回一个新的Promise,可以并成then()链式调用
2) 通过then的链式调用串联多个同步/异步任务

let p = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("OK");
}, 1000);
});
p.then(value => {
return new Promise((resolve, reject) => {
resolve("success");
});
}).then(value => {
console.log(value); // Success
}).then(value => {
console.log(value); // undefined
});

##### 链式调用案例2

new Promise((resolve, reject) => {
setTimeout(() => {
console.log('执行任务1(异步)')
resolve(1)
}, 1000)
}).then(
value => {
console.log('任务1的结果', value)
console.log('执行任务2(同步)')
return 2 // 同步任务直接return返回结果
}
).then(
value => {
console.log('任务2的结果', value)
return new Promise((resolve, reject) => { // 异步任务需要包裹在Promise对象中
setTimeout(() => {
console.log('执行任务3(异步)')
resolve(3)
}, 1000)
})
}
).then(
value => {
console.log('任务3的结果', value)
}
)
// 执行任务1(异步)
// 任务1的结果 1
// 执行任务2(同步)
// 任务2的结果 2
// 执行任务3(异步)
// 任务3的结果 3


#### Promise异常穿透(传透)?
1) 当使用Promise的then链式调用时,可以在最后指定失败的回调;
2)前面任何操作除了异常,都会传到最后失败的回调中处理;

new Promise((resolve, reject) => {
//resolve(1)
reject(1)
}).then(
value => {
console.log('onResolved1()', value)
return 2
}
).then(
value => {
console.log('onResolved2()', value)
return 3
}
).then(
value => {
console.log('onResolved3()', value)
}
).catch(
reason => {
console.log('onRejected1()', reason)
}
)
// onRejected1() 1
// 或者
new Promise((resolve, reject) => {
//resolve(1)
reject(1)
}).then(
value => {
console.log('onResolved1()', value)
return 2
},
reason => {throw reason} // 抛出失败的结果reason
).then(
value => {
console.log('onResolved2()', value)
return 3
},
reason => {throw reason} // 抛出失败的结果reason
).then(
value => {
console.log('onResolved3()', value)
},
reason => {throw reason} // 抛出失败的结果reason
).catch(
reason => {
console.log('onRejected1()', reason)
}
)
// onRejected1() 1
// 所以失败的结果是一层一层处理下来的,最后传递到 catch 中。
// 或者,将 reason => {throw reason} 替换为 reason => Promise.reject(reason) 也是一样的


# 如何中断Promise的链?
当使用Promise的then链式调用时,在中间中断,不再调用后面的回调函数
办法:在回调函数中返回一个pending状态的promise对象

new Promise((resolve, reject) => {
//resolve(1)
reject(1)
}).then(
value => {
console.log('onResolved1()', value)
return 2
}
).then(
value => {
console.log('onResolved2()', value)
return 3
}
).then(
value => {
console.log('onResolved3()', value)
}
).catch(
reason => {
console.log('onRejected1()', reason)
}
).then(
value => {
console.log('onResolved4()', value)
},
reason => {
console.log('onRejected2()', reason)
}
)
// onRejected1() 1
// onResolved4() undefined
// 为了在catch中就中断执行可以这样写:
new Promise((resolve, reject) => {
//resolve(1)
reject(1)
}).then(
value => {
console.log('onResolved1()', value)
return 2
}
).then(
value => {
console.log('onResolved2()', value)
return 3
}
).then(
value => {
console.log('onResolved3()', value)
}
).catch(
reason => {
console.log('onRejected1()', reason)
return new Promise(() => {}) // 返回一个pending的promise
}
).then(
value => {
console.log('onResolved4()', value)
},
reason => {
console.log('onRejected2()', reason)
}
)
// onRejected1() 1

> 在catch中返回一个新的Promise,且这个Promise没有结果。
> 由于,返回的新的Promise结果决定后面then中的结果,所以后面的then中也没有结果
> 就实现了中断Promise链的效果。

标签:resolve,console,log,value,reason,理解,Promise,使用
From: https://www.cnblogs.com/openmind-ink/p/16971453.html

相关文章