第11章 期约与异步函数

第十一章,期约与异步函数

散碎

.resolve() 是幂等的,.reject() 不是
const origin = Promise.resolve('inner');

const p = Promise.resolve(origin);
console.log(p === origin, p); // true Promise<fulfilled>: 'inner'

const p2 = Promise.reject(origin);
console.log(p2 === origin, p2); // false Promise<rejected>: Promise
外部 try catch 无法捕捉错误
try {
  Promise.reject(1);
} catch (error) {
  console.log(error); // 捕获不到,会报错
}
多个串联的期约处理,可以理解为树接口,执行顺序可以理解为层序遍历
// 		  A
// 		/   \
// 	 B     C
//  / \   / \
// D   E F   G
let A = new Promise((resolve, reject) => {
  console.log('A');
  resolve();
});
let B = A.then(() => console.log('B'));
B.then(() => console.log('D'));
let C = A.then(() => console.log('C'));
C.then(() => console.log('F'));
B.then(() => console.log('E'));
C.then(() => console.log('G'));
// A B C D E F G
有方法会静默处理错误(.all、.race

后续解决或拒绝的期约将不再影响返回的期约的状态。这种行为称为“静默处理”,意味着后续的拒绝不会触发任何错误或被捕获的拒绝处理程序。(catch 会触发的话,只能被触发一次

let p = Promise.race([Promise.reject(1), new Promise((_, reject) => reject(2))]);
p.catch(reason => setTimeout(console.log, 0, reason)); // 1 (之后不再处理2
串行期约组合合并处理
const add_2 = x => x + 2;
const add_4 = x => x + 4;
const add_8 = x => x + 8;
const fns = [add_2, add_4, add_8];
const compose = fns => {
  return x => fns.reduce((p, fn) => p.then(fn), Promise.resolve(x));
};
const flow = compose(fns);
flow(1).then(result => {
  console.log(result);
});

期约取消与进度

es6+ 并没有实现取消与进度,委员会认为取消与进度是激进的;如果实现取消与进度,会让期约连锁和合成过度复杂

期约取消

我不知道为何书上要那样写,感觉总是不直观,以下借鉴进度的写法实现取消

<button id="start">Start</button>
<button id="cancel">Cancel</button>
<script>
  const startBtn = document.querySelector('#start')
  const cancelBtn = document.querySelector('#cancel')
  class CouldCancelPromise {
    constructor(executor) {
      // 保留原来的 resolve,reject 并添加 cancel 函数
      return new Promise((resolve, reject) => {
        executor(resolve, reject, resolve)
      })
    }
  }
  startBtn.addEventListener('click', () => {
    console.log('wait...')
    const cancelPromise = new CouldCancelPromise((resolve, reject, cancel) => {
      const id = setTimeout(resolve, 3000, 'done')
      cancelBtn.addEventListener('click', () => {
        cancel('cancel')
        clearInterval(id)
      })
    })
    cancelPromise.then(result => {
      console.log(result)
    })
  })
</script>

也可以采用继承的形式(不推荐继承内置对象)

class CouldCancelPromise extends Promise {
  constructor(executor) {
    super((resolve, reject) => {
      executor(resolve, reject, resolve);
    });
  }
}
期约进度,就如 fetch 的下载进度一样,大致代码如下
class ProgressPromise extends Promise {
  constructor(executor) {
    const notifyHandlers = [];
    super((resolve, reject) => {
      return executor(resolve, reject, progress => {
        notifyHandlers.forEach(notify => {
          notify(progress);
        });
      });
    });
    this.notifyHandlers = notifyHandlers;
  }
  notify = function (fun) {
    this.notifyHandlers.push(fun);
    return this;
  };
}
const p = new ProgressPromise((resolve, reject, notify) => {
  function getData(step) {
    if (step < 5) {
      notify(`${step * 20} %.........`);
      setTimeout(getData, 1000, step + 1);
    } else {
      resolve('done');
    }
  }
  getData(0);
});
p.notify(progress => {
  console.log(progress);
});
p.then(res => {
  console.log(res);
});
/*
20 %.........
40 %.........
60 %.........
80 %.........
done
*/

异步函数

async

用于定义异步函数,只在异步函数内才能使用 await

await

await 可以展平 thenable 对象

async function foo() {
  const origin = 'xz';
  const obj = {};
  const thenable = {
    then(callback) {
      callback('thenable');
    },
  };
  const p = Promise.resolve('resolve');
  console.log(await origin, await obj, await thenable, await p);
  await Promise.reject('reject');
  console.log('end'); // 不执行
}
foo().catch(console.log);
// xz {} thenable resolve reject

实现 sleep

function sleep(delay) {
  return new Promise(resolve => setTimeout(resolve, delay));
}
async function main() {
  console.log(1);
  await sleep(1000);
  console.log(2);
}
main();