Promise

113 阅读11分钟

Promise的理解

Promise是异步编程的解决方案,比传统的解决方案——回调函数和事件——更合理更强大。

Promise是一个构造函数,它会接收一个函数作为参数,并且返回一个Promise实例,可以通过它获取异步操作的信息;它也有提供统一的API,各种异步操作都可以通过同样的方式进行处理。

Promise有三个状态

  • pending:进行中
  • resolved(fulfilled):成功
  • rejected:失败

当把一件事交给Promise时,它就是起初的状态pending,当任务完成了状态也就变成了resolved, 而任务失败了状态就会变成rejected,所以Promise只有两个过程

  • pending--->resolved
  • pending--->rejected

注意:一旦状态进行了改变就不会再改变。

Promise的优点

  • 指定回调函数方式会更加灵活
  • 支持链式调用,可以解决回调地狱的问题
  • 对象的状态不会受外界影响,只有异步的操作才能决定当前是哪一种状态,其他任何操作都无法改变这个状态。
  • 一旦状态改变就不会再变,并且任何时候都可以得到这个结果。

Promise的缺点

  • 无法取消Promise,一旦新建它就会立刻执行,中途没有办法取消
  • 如果不设置回调函数的话,Promise内部抛出的错误,不会反应到外部去
  • 当处于刚开始的pending状态时,无法得知目前进展到哪一个阶段了

如何使用Promise

Promise构造函数

Promise(executor){}

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

Promise会接收一个函数作为参数,而这个函数又会有两个参数,分别是resolvereject

const p = new Promise((resolve, reject) => {
    //do something
    if (/*异步操作成功*/) {
        resolve()
    } else {
        reject()
    }
})

Promise.prototype.then()

p.then(onResolved, onRejected)

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

then()方法是用来调用之前创建好的Promise对象.

then()方法会接收两个回调函数作为参数,第一个回调函数是Promise对象的状态变为resolved时调用,第二个回调函数是Promise对象的状态变为resolved时调用;其中第二个参数可以省略。

这两个回调函数中还可以接收一个参数,而这个参数的值就是在创建Promise对象时,调用resolve或者reject方法所带的值

then()方法会返回一个新的Promise实例(并不是之前的那个Promise实例),所以可以采取链式写法,即then()方法后面再调用一个then()方法。

// 无传参
const p = new Promise((resolve, reject) => {
    // do something
    if (/*异步操作成功*/) {
        resolve()
    } else {
        reject()
    }
});

p.then(() => {
    // 成功后执行的操作
}, () => {
    // 失败后执行的操作
})

// 有传参
const p = new Promise((resolve, reject) => {
    // do something
    if (/*异步操作成功*/) {
        resolve(/*参数*/)
    } else {
        reject(/*参数*/)
    }
})

p.then((value) => {      /*value是上面成功后resolve传递过来的参数*/
    // 成功后执行的操作
}, (reason) => {		/*reason是上面失败后reject传递过来的参数*/
    // 失败后执行的操作
})

console.log(p)	//返回的是一个Promise对象

例:

//成功
const pOne = new Promise((resolve, reject) => {
    resolve('这是成功了')
})
pOne.then((value) => {
    console.log(value)  // 这是成功了
}, (reason) => {
    console.log(reason)	//上面的状态是成功,所以此处无输出
})

console.log(pOne);     // Promise { <resolved>: "这是成功了" }

//失败
const pTwo = new Promise((resolve, reject) => {
    reject('这是失败了')
})
pTwo.then((value) => {
    console.log(value)  // 同理上面的状态是失败,所以此处无输出
}, (reason) => {
    console.log(reason);	//这是失败了
})

console.log(pTwo);       // Promise {<rejected>: '这是失败了'}

在这里插入图片描述

Promise.prototype.catch()

p.catch(onRejected)

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

Promise.prototype.catch()方法是.then(null, rejection).then(undefined, rejection)的别名(是then()的语法糖),用于指定发生错误时的回调函数

const pOne = new Promise((resolve, reject) => {
    resolve('成功的数据') // resolve()函数
})

const pTwo = new Promise((resolve, reject) => {
    reject('失败的数据') //reject()函数
})

pOne.then((value) => {         // onResolved()函数
    console.log(value)      // 成功的数据
}).catch((reason) => {      // onRejected()函数
    console.log(reason)     // 失败的数据
})

pTwo.then((value) => {         // onResolved()函数
    console.log(value)      // 成功的数据
}).catch((reason) => {      // onRejected()函数
    console.log(reason)     // 失败的数据
})

在这里插入图片描述

Promise.resolve()

Promise.resolve(value)

  • value:将被 Promise 对象解析的参数,也可以是一个成功或失败的 Promise 对象

Promise.resolve()的返回值也是一个promise对象,可以对返回值进行.then()方法的调用

  • 如果传入的参数是一个非promise,则返回的就是成功的promise对象,结果是参数值;

例:

const p = Promise.resolve(521);
console.log(p); // Promise {<fulfilled>: 521}

在这里插入图片描述

  • 如果传入的参数是一个promise对象,则该promise对象返回的结果就决定了resolve的结果;

例:

//传入的promise的结果是一个成功的
const pOne = Promise.resolve(new Promise((resolve, reject) => {
    resolve('OK')       //成功
}));

console.log(pOne);     //Promise {<fulfilled>: 'OK'}

//传入的promise的结果是一个失败的
const pTwo = Promise.resolve(new Promise((resolve, reject) => {
    reject('ERROR')     //失败
}));

console.log(pTwo);     //Promise {<rejected>: 'ERROR'}

在这里插入图片描述

Promise.reject()

Promise.resolve(reason)

  • reason:失败的原因

它将返回一个失败的promise对象;

  • 如果传入的参数是非promise,则返回的就是一个失败的promise对象,结果 就是该参数值
//传入的参数是一个非promise
const p = Promise.reject(521)

console.log(p);     //Promise {<rejected>: 521}

在这里插入图片描述

  • 如果传入的参数是一个promise,则返回的是一个失败的promise对象,而结果是传入的这个promise返回的结果;
// 传入的参数是一个成功的promise
const pOne = Promise.reject(new Promise((resolve, reject) => {
    resolve('OK')       //成功
}));

console.log(pOne);     //Promise {<rejected>: Promise}

// 传入的对象是一个失败的promise
const pTwo = Promise.reject(new Promise((resolve, reject) => {
    reject('ERROR')     //失败
}));

console.log(pTwo);     //Promise {<rejected>: Promise}

在这里插入图片描述

Promise.all()

Promise.all(iterable)

  • iterable:包含 n 个 promise 的可迭代对象,如 Array 或 String

它将会返回一个新的promise,但是只有全部的promise都成功了,返回的新的promise的结果才是成功,否则就是失败

  • 若传入的promise的结果都是成功,则返回的一个成功的promise,结果就是含有传入的全部promise的结果值的一个数组。
  • 若传入的promise的结果有一个或更多的失败,则返回的就是一个失败的promise,结果就是传入的promise中第一个失败的结果值。
//全成功
let pOne = new Promise((resolve, reject) => {
    resolve('OK');
})
let pTwo = Promise.resolve('Success');
let pThree = Promise.resolve('Oh Yeah');

const resultOne = Promise.all([pOne, pTwo, pThree]);
console.log(resultOne);

//有一个失败
let pFour = new Promise((resolve, reject) => {
    resolve('OK');
})
let pFive = Promise.reject('Oh No 1');
let pSix = Promise.reject('Oh No 2');
const resultTwo = Promise.all([pFour, pFive, pSix]);
console.log(resultTwo);

在这里插入图片描述

Promise.race()

Promise.race(iterable)

iterable:包含 n 个 promise 的可迭代对象,如 Array 或 String

它会返回一个新的promise,结果是第一个完成promise的结果状态以及值(谁先完成输出谁,不管是成功还是失败)

// pOne 延迟完成  全部都为成功
let pOne = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('OK');
    }, 1000);
})
let pTwo = Promise.resolve('Success');
let pThree = Promise.resolve('Oh Yeah');

//调用
const resultOne = Promise.race([pOne, pTwo, pThree]);

console.log(resultOne);


// 谁先完成就输出谁(不管是成功还是失败)
// pFour延迟完成,pFive为失败
const pFour = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve(1)
    }, 1000)
})
const pFive = Promise.reject(2)
const pSix = Promise.resolve(3)

const resultTwo = Promise.race([pFour, pFive, pSix])

在这里插入图片描述

Promise的几个关键问题

1、如何改变promise的状态?

(1)resolve(value):如果当前是 pending 就会变为 resolved

(2)reject(reason):如果当前是 pending 就会变为 rejected

(3)抛出异常:如果当前是 pending 就会变为 rejected

// 成功
const pOne = new Promise((resolve, reject) => {
    resolve(1) // promise变为resolved成功状态
})
pOne.then(
    value => { console.log('value', value) },
    reason => { console.log('reason', reason) }
)
console.log('pOne', pOne);


// 失败
const pTwo = new Promise((resolve, reject) => {
    reject(2) // promise变为rejected失败状态
})
pTwo.then(
    value => { console.log('value', value) },
    reason => { console.log('reason', reason) }
)
console.log('pTwo', pTwo);

// 抛出错误
const pThree = new Promise((resolve, reject) => {
    throw new Error('出错了') // 抛出异常,promise变为rejected失败状态,reason为抛出的error
})
pThree.then(
    value => { console.log('value', value) },
    reason => { console.log('reason', reason) }
)
console.log('pThree', pThree);

在这里插入图片描述

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

promise的状态改变为相对应的状态时就会调用

// 状态为成功时
const pOne = new Promise((resolve, reject) => {
    resolve('成功')
})
pOne.then(
    value => { console.log('value1', value) },
    reason => { console.log('reason1', reason) }
)
pOne.then(
    value => { console.log('value2', value) },
    reason => { console.log('reason2', reason) }
)
console.log(pOne);


// 状态为失败时
const pTwo = new Promise((resolve, reject) => {
    reject('失败')
})
pTwo.then(
    value => { console.log('value1', value) },
    reason => { console.log('reason1', reason) }
)
pTwo.then(
    value => { console.log('value2', value) },
    reason => { console.log('reason2', reason) }
)
console.log(pTwo);

在这里插入图片描述

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

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

  • 如何先改变状态再指定回调?

(1)在执行器中直接调用resolve()/reject()

(2)延迟更长时间再调用then()

例:

const p = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve('成功')		//这个是改变状态
    }, 1000)   //有定时器(异步)就先指定回调再改变状态
})
p.then(		//这个是调用回调
    value => { console.log('value1', value) },
    reason => { console.log('reason1', reason) }
)
  • 什么时候才能得到数据?

(1)如果先指定的回调,那当状态发生改变时,回调函数就会调用得到数据;

(2)如果先改变的状态,那当指定回调时,回调函数就会调用得到数据。

//先指定了回调函数,保存了当前指定的回调函数,再改变了状态(同时指定数据),然后异步执行之前保存的回调函数。
new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve(1) // 改变状态
  }, 1000)
}).then( // 指定回调函数 (先指定)
  value => {},
  reason =>{}
)
//这个是先改变了状态(同时指定了数据),然后指定回调函数,并且直接异步执行回调函数。
new Promise((resolve, reject) => {
  resolve(1) // 改变状态
}).then( // 指定回调函数
  value => {},
  reason =>{}
)

4、promise.then() 返回的新 promise 的结果状态由什么决定?

(1)简单的来说:它的结果是由then()所指定的回调函数执行的结果来决定的;

const p = new Promise((resolve, reject) => {
        resolve('成功')
})
const result = p.then(
    value => { console.log('value1', value) },
    reason => { console.log('reason1', reason) }
)
console.log(result);

在这里插入图片描述

(2)详细的来说:

  • 如果抛出异常,则promise.then() 返回的新 promise的状态变为rejected(失败),结果值为抛出错误的值
const p = new Promise((resolve, reject) => {
    resolve('成功')
})
const result = p.then(value => {
    throw '失败'	//抛出异常
}, reason => {
    console.log('reasons', reason)
}
)
console.log(result);

在这里插入图片描述

  • 如果返回的是一个非promise,则promise.then() 返回的新 promise的状态为resolve(成功),结果值为该非promise值
const p = new Promise((resolve, reject) => {
    resolve('成功')
})
const result = p.then(value => {
    return 521		//返回一个非promise
}, reason => {
    console.log('reasons', reason)
}
)
console.log(result);

在这里插入图片描述

  • 如果返回的是一个promise对象,则promise.then() 返回的新 promise的状态就是上面这个返回的promise对象的状态,结果值也是上面这个返回的promise对象的值;
const p = new Promise((resolve, reject) => {
    resolve('成功')
})
const resultOne = p.then(value => {
    return new Promise((resolve, reject) => {	//返回一个promise
        resolve('success')
    })
}, reason => {
    console.log('reasons', reason)
})

const resultTwo = p.then(value => {
    return new Promise((resolve, reject) => {	//返回一个promise
        reject('error')
    })
}, reason => {
    console.log('reasons', reason)
})

console.log(resultOne);
console.log(resultTwo);

在这里插入图片描述 例:

new Promise((resolve, reject) => {
  resolve(1)
}).then(
  value => {
    console.log('onResolved1()', value)
    //return 2                   // onResolved2() 2
    //return Promise.resolve(3)  // onResolved2() 3
    //return Promise.reject(4)   // onRejected2() 4
    //throw 5                    // onRejected2() 5
  },
  reason => {
    console.log('onRejected1()', reason)
  }
).then(
  value => {
    console.log('onResolved2()', value)
  },
  reason => {
    console.log('onRejected2()', reason)
  }
)
// onResolved1() 1
// onResolved2() undefined
// Promise {<fulfilled>: undefined}
// 对应输出如上所示

因为返回的是一个promise对象,其也可以用then()方法,而使用then()方法会根据该promise对象的状态去调用resolve()或者reject(),而结果值就是该promise对象的结果值。

5、promise 如何串联多个操作任务?

(1)promisethen()会返回一个新的promise,可以接着使用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) 通过使用then()的链式调用来串联多个同步/异步任务

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

6、Promise 异常穿透?

(1)当使用 promisethen() 链式调用时,可以在最后指定失败的回调

//这样的写法,当失败时直接在最后接收失败的结果
new Promise((resolve, reject) => {
   //resolve(1)		//依次输出1,2,3
   reject(1)		// onRejected1() 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)
  }
)

(2)前面任何操作出了异常,都会传到最后失败的回调中处理

//这样的写法,是将失败的结果一层一层的传递到最后
new Promise((resolve, reject) => {
   //resolve(1)		//依次输出1,2,3
   reject(1)		// onRejected1() 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)
  }
)

7、中断 promise 链?

当使用promisethen()链式调用时,在中间中断,不在调用后面的回调函数

办法:在回调函数中返回一个pending状态的promise对象

const p = new Promise((resolve, reject) => {
    resolve('成功')
})

// 中断Promise链
p.then((value) => {
    console.log('value1', value);
    return new Promise(() => { })   //直接返回一个pending状态的Promise对象,也可以写在catch中
}).then((value) => {                //后续的then不会执行
    console.log('value2', value);
}).then((value) => {
    console.log('value3', value);
}).catch((reason) => {
    console.log('reason', reason);
})

console.log(p);

由于,promise的状态决定了后面then()中的结果,当返回一个pending状态的promise时,后续的then()也就不会去执行。