Skip to content
On this page

异步编程

状态

  • 状态值
    • pending 连接状态
    • fulfilled 成功状态
    • reject 拒绝状态
  • 状态是不可逆的,状态谁先改变则是谁
  • 有两个回调函数
    • resolve 成功
    • reject 失败

then

  • 包含两个回调函数,一个成功回调,一个失败回调
js
new Promise((resolve, reject) => {
    resolve('成功')
}).then(result => {
    console.log(result)
})
new Promise((resolve, reject) => {
    reject('失败')
}).then(null, reason => {
    console.log(reason)
})
  • 链式调用,优先执行先定义的 then 方法

    js
    new Promise((resolve, reject) => {
        resolve('成功')
    }).then(res => {
        console.log(res)
    }).then(res => {
        console.log(res)
    })
    // 成功, undefined
  • 每个 then 方法返回的也是一个 promise

    js
    let p1 = new Promise((resolve, reject) => {
        resolve('成功')
    })
    
    let p2 = p1.then(value => {
        console.log(value)
    }, reason => {
        console.log(reason)
    })
    
    setTimeout(() => {
        console.log(p1)
        console.log(p2)
    })
    
    // ’成功', p1, p2
  • 多个 then 方法时,下一个 then 方法是对上一个 then 返回(return)的值进行处理,且默认状态是 resolve 成功

    js
    new Promise((resolve, reject) => {
        reject("失败");
    }).then(
        result => console.log(result),
        reason => console.log(reason)
    ).then(
        result => console.log("成功"),
        reason => console.log("失败")
    )
    // 失败, 成功
    
    new Promise((resolve, reject) => {
        resolve("成功");
    }).then(
        result => {
            return new Promise((resolve, reject) => {
                reject('第二个错误promise')
            })
        },
        reason => console.log(reason)
    ).then(
        result => console.log("成功"),
        reason => console.log('error ' + reason)
    )
    // error 第二个错误promise
  • 其它类型的 promise 封装

    js
    new Promise((resolve, reject) => {
        resolve("成功");
    }).then(
        result => {
            class Fn {
               then(resolve, reject){
                    resolve('这是对象')
                } 
            }
            return new Fn()
        }
    ).then(
        result => console.log(result),
        reason => console.log('error ' + reason)
    )
    // 这是对象

catch

  • 可以捕获 promise 抛出的错误
  • 建议统一将方法放在最后执行捕获
  • 前面 then 方法做了错误回调处理,则不执行 catch 方法
js
new Promise((resolve, reject) => {
    reject("失败");
  }).then(
    result => console.log(result),
    reason => console.log('error ' + reason)
  ).catch(err => {
    console.log(err);
  }) 
// error 失败

finally

无论状态是resolvereject 都会执行此动作,finally 与状态无关。

js
new Promise((resolve, reject) => {
    resolve("成功");
}).then(
    result => console.log(result)
).catch(err => {
    console.log(err);
}).finally(() => {
    console.log("resolve/reject状态都会执行")
})
// 成功, resolve/reject状态都会执行

使用promise异步加载图片

js
function loadImage(src, id) {
    return new Promise((resolve, reject) => {
        const image = new Image()
        image.src = src
        image.onload = () => {
            resolve(image)
        }
        image.onerror = reject
        document.querySelector(id).appendChild(image)
    })
}

定时器封装

js
function timeout(delay = 1000) {
    return new Promise(resolve => setTimeout(resolve, delay))
}
timeout(2000).then(() => {
   console.log('2秒后输出')
   return timeout(1000)
}).then(() => {
    console.log('再过1秒后输出')
})

其他接口方法

resolve

使用 promise.resolve 方法可以快速的返回一个 promise 对象

js
Promise.resolve("Mondo").then(value => {
  console.log(value); // Mondo
});

缓存查询数据

js
function query(name) {
  const cache = query.cache || (query.cache = new Map());
  if (cache.has(name)) {
    console.log("缓存");
    return Promise.resolve(cache.get(name));
  }
  return ajax(`http://test.com?name=${name}`).then(
    res => {
      cache.set(name, response);
      console.log("没走缓存");
      return res;
    }
  );
}
query("Mondo").then(res => {
  console.log(res);
});

reject

Promise.resolve 类似,reject 生成一个失败的promise

js
new Promise((resolve, reject) => {
    resolve('成功')
}).then(value => {
    if (value !== 'mondo') {
        return Promise.reject('参数错误')
    }
}).catch(error => {
    console.log(error) // 参数错误
})

all

使用Promise.all 方法可以同时执行多个并行异步操作

  • 任何一个 Promise 执行失败就会调用 catch方法
  • 适用于一次发送多个异步操作
  • 参数必须是可迭代类型,如Array/Set
  • 成功后返回 promise 结果的有序数组
js
const p1 = new Promise((resolve, reject) => {
  resolve("第一个Promise");
});
const p2 = new Promise((resolve, reject) => {
  resolve("第二个Promise");
});
Promise.all([p1, p2]).then(res => {
  console.log(res); // ['第一个Promise', '第二个Promise']
}).catch(error => {
  console.log(error);
});

allSettled

Promise.allSettled 用于处理多个promise ,只关注执行完成,不关注是否全部执行成功,allSettled 状态只会是fulfilled

js
const p1 = new Promise((resolve, reject) => {
  resolve("第一个Promise");
});
const p2 = new Promise((resolve, reject) => {
  reject("第二个Promise");
});
Promise.allSettled([p1, p2]).then(res => {
  console.log(res);
})

打印出的来值为

race

Promise.race 批量处理多个 Promise,谁先返回状态就执行哪个

  • 以最先返回的 promise 为准
  • 如果最先返加的状态为rejected 那么整个promiserejected执行catch
  • 如果参数不是promise,内部将自动转为promise
js
const p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("第一个Promise");
  }, 1000)
});
const p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject("第二个Promise");
  })
});
Promise.race([p1, p2]).then(res => {
  console.log(res);
}).catch(error => {
  console.log(error) // 第二个Promise
})

任务队列

如果 then 返回一个 promise 时,后面的 then 就是对返回的 promise 进行处理,只有当一个 promise 状态 resolve 后,下一个 then 方法才会执行

js
let promise = Promise.resolve();
let p1 = promise.then(() => {
  return new Promise(resolve => {
    setTimeout(() => {
      console.log(`p1`);
      resolve();
    }, 2000);
  });
});
p1.then(() => {
  return new Promise((a, b) => {
    setTimeout(() => {
      console.log(`p2`);
    }, 1000);
  });
})
// p1, p2

实现一个队列输出

js
// 使用map
function queue(num) {
  let promise = Promise.resolve()
  num.map(v => {
    promise = promise.then(_ => {
      return new Promise(resolve => {
        setTimeout(() => {
          console.log(v)
          resolve()
        }, 1000)
      })
    })
  })
}
// 使用reduce
function queue(num) {
  let promise = Promise.resolve()
  num.reduce((promise, v) => {
    return promise.then(_ => {
      return new Promise(resolve => {
        setTimeout(() => {
          console.log(v)
          resolve();
        }, 1000)
      })
    })
  }, Promise.resolve())
}
queue([1, 2, 3, 4, 5, 6])

async与await语法糖

async

在函数前加上 async,函数将返回一个 promise,可以像使用 promise 一样使用

  • 无论函数有没有返回值,promise 状态都是 resolve 完成状态
js
async function fn() {
    return 'mondo'
}
console.log(fn())
fn().then(res => {
    console.log(re)
})

await

使用 await 会等待 promise 执行完

  • await 后面一般是promise,如果不是直接返回
  • await 必须放在 async 定义的函数中使用
  • await 用于替代 then 使编码更优雅
js
async function fn() {
  let promise = new Promise(resolve => {
    setTimeout(() => {
      resolve('mondo')
    }, 1000)
  })
  let a = await promise
  console.log(a)
}
fn()

async 延时函数

js
async function sleep(delay = 2000) {
    return new Promise(resolve => {
        setTimeout(() => {
            resolve()
        }, delay)
    })
}
async function show(arr) {
    for (const v of arr) {
        await sleep()
        console.log(v)
    }
}
show([1, 2])

class 与 await 的结合

js
class User {
    constructor(name) {
        this.name = name
    }
    // 定义then方法,返回 resolve 状态
    then(resolve, reject) {
        setTimeout(() => {
            resolve(this.name)
        }, 1000)
    }
}
async function get() {
    let user = await new User('mondo')
    console.log(user)
}
get() // mondo

async 错误处理

  • 使用 catch 捕获错误

    js
    async function fn() {
        throw new Error('error')
    }
    fn().catch(err => {
        console.log(err)
    })
  • 使用 try...catch 捕获错误

    js
    async function fn() {
        try {
            const a = await fn1()
          	const b = await fn2()
            return b
        } catch (error => {
            console.log(error)
    	})
    }
    fn()

await 并行执行

js
async function p1() {
    return 'mondo'
}
async function p2() {
    return 'imondo.cn'
}
async function fn() {
    const h1 = p1()
    const h2 = p2()
    console.log(h1, h2)
    const v1 = await h1
    const v2 = await h2
    console.log(v1, v2)
}
fn() // 同时打印 mondo imondo.cn

// 或者

async function fn1() {
  const res = await Promise.all([p1(), p2()])
  console.log(res)
}
fn1() // 同时打印 ['mondo', 'imondo.cn']

宏任务与微任务执行顺序

  • 同步任务优先执行

  • 微任务优先宏任务执行

  • 微任务 promise

  • 宏任务 setTimeout

js
setTimeout(() => {
    console.log(1)
}, 0)
new Promise(reslove => {
    reslove()
    console.log(2)
}).then(() => {
    console.log(3')
})
console.log(4) // 2,4,3,1

手写一个Promise

  • 实现promise基本用法
  • 实现then链式调用方法
  • 实现all方法
  • 实现race方法
js
class MoPromise {
  static PENDING = "pending";
  static FULFILLED = "fulfilled";
  static REJECT = "reject";
  constructor(executor) {
    this.status = MoPromise.PENDING;
    this.value = null;
    this.callbacks = [];
    // 出现异常时执行reject方法
    try {
      executor(this.resolve.bind(this), this.reject.bind(this));
    } catch (error) {
      this.reject(error);
    }
  }

  resolve(value) {
    // 只有pending状态下才执行,且状态变了后不可修改
    if (this.status === MoPromise.PENDING) {
      this.status = MoPromise.FULFILLED;
      this.value = value;
      setTimeout(() => {
        this.callbacks.map(callback => {
          callback.onFulfilled(value);
        });
      });
    }
  }

  reject(reason) {
    if (this.status === MoPromise.PENDING) {
      this.status = MoPromise.REJECT;
      this.value = reason;
      setTimeout(() => {
        this.callbacks.map(callback => {
          callback.onReject(reason);
        });
      });
    }
  }

  then(onFulfilled, onReject) {
    // 当回调函数没传时,定义默认函数
    if (typeof onFulfilled !== "function") {
      onFulfilled = () => this.value; // then穿透传值
    }
    if (typeof onReject !== "function") {
      onReject = () => this.value;
    }
    // 返回promise,实现链式操作
    let promise = new MoPromise((resolve, reject) => {
      // 准备状态处理,以后需要执行的任务队列
      if (this.status === MoPromise.PENDING) {
        this.callbacks.push({
          onFulfilled: value => {
            this.parse(promise, onFulfilled(value), resolve, reject);
          },
          onReject: value => {
            this.parse(promise, onReject(value), resolve, reject);
          }
        });
      }
      // 只有状态改变 才执行方法
      if (this.status === MoPromise.FULFILLED) {
        // 放置在任务队列,实现异步操作
        setTimeout(() => {
          this.parse(promise, onFulfilled(this.value), resolve, reject);
        });
      }

      if (this.status === MoPromise.REJECT) {
        setTimeout(() => {
          this.parse(promise, onReject(this.value), resolve, reject);
        });
      }
    });
    return promise;
  }

  parse(promise, result, resolve, reject) {
    if (promise === result) {
      throw new TypeError("不可返回相同类型");
    }
    try {
      // 如果then返回promise
      if (result instanceof MoPromise) {
        result.then(resolve, reject);
      } else {
        resolve(result);
      }
    } catch (error) {
      reject(error);
    }
  }

  static resolve(value) {
    return new MoPromise((resolve, reject) => {
      if (value instanceof MoPromise) {
        value.then(resolve, reject);
      } else {
        resolve(value);
      }
    });
  }

  static reject(value) {
    return new MoPromise((resolve, reject) => {
      if (value instanceof MoPromise) {
        value.then(resolve, reject);
      } else {
        reject(value);
      }
    });
  }

  static all(promises) {
    const values = [];
    return new MoPromise((resolve, reject) => {
      promises.forEach(promise => {
        promise.then(
          value => {
            values.push(value);
            if (values.length === promises.length) {
              resolve(values);
            }
          },
          reason => {
            reject(reason);
          }
        );
      });
    });
  }

  static race(promises) {
    return new MoPromise((resolve, reject) => {
      promises.forEach(promise => {
        promise.then(
          value => {
            resolve(value);
          },
          reason => {
            reject(reason);
          }
        );
      });
    });
  }
}