[Основы ES6] обещание и ожидание/асинхронность

JavaScript

Причина написания этой статьи:

  1. Асинхронность — это высокочастотный сценарий приложений, который должен быть повсеместным.
  2. Обычно накопленные знания относительно разбросаны и не образуют целостной системы, поэтому специализируйте их и применяйте на практике, чтобы освоить неоднозначные точки знаний.
  3. Выстраивайте отношения между разумом, телом и эмоциями
  4. Ежедневная зарядка меняет качество жизни

Смотрите квитанцию ​​этой статьи:

  1. Поймите точки знаний, которые нелегко заметить в Promise и await/async.
  2. Часто используемые сценарии использования

Для продвинутых, пожалуйста, смотрите мою другую статьюСводка промисов и ожидания асинхронности

Я думаю, что точки знаний этих двух статей немного повторяются, потому что при подведении итогов и размышлениях должно быть пересечение, поэтому я думаю, что качество статьи: лучшего нет, только лучше.

Если появятся новые точки знаний, я продолжу добавлять их позже, и исправления также приветствуются.

Вот небольшая мысль в моем сердце: я надеюсь выводить очень привлекательные статьи, я надеюсь, что это принесет пользу другим и мне, я надеюсь, что все поставят 👍, не тратя денег!

1. Обещания

Прежде чем писать текст, возьмем в качестве примера интерфейс, который можно запросить из внешней сети

https://api.github.com/users/github

function get(url, params, config = {}) {    
  return new Promise((resolve, reject) => {
    axios.get(url, {
      params,
      ...config,
    }).then(res => {
      resolve(res.data);
    }).catch(err => {
      reject(err.data);
    })    
  });
}

后文请求得到的值用  【"真实请求到的值"】代替

1. Основное использование

1.1 Состояние p1 как p2

const p1 = new Promise(function (resolve, reject) {
  setTimeout(() => reject(new Error('fail')), 3000)
})

const p2 = new Promise(function (resolve, reject) {
  setTimeout(() => resolve(p1), 1000)
})

p2
  .then(result => console.log(result))
  .catch(error => console.log(error))
// Error: fail

p1 — это промис, который отклоняется через 3 секунды. Состояние p2 изменяется через 1 секунду, и метод разрешения возвращает p1. Поскольку p2 возвращает другое обещание, состояние самого p2 недействительно, а состояние p1 определяет состояние p2. Следовательно, все следующие утверждения then становятся для последнего (p1). Еще через 2 секунды p1 становится отклоненным, что приводит к срабатыванию функции обратного вызова, указанной в методе catch.

1.2 Функция после разрешения также будет выполнена

new Promise((resolve, reject) => {
  resolve(1);
  console.log(2);
}).then(r => {
  console.log(r);
});
// 2
// 1

1.3 разрешить плюс возврат, последний больше не будет выполняться

new Promise((resolve, reject) => {
  return resolve(1);
  // 后面的语句不会执行
  console.log(2);
})

Вообще говоря, после вызова разрешения или отклонения миссия Promise завершается, и последующие операции должны быть помещены в метод then, а не сразу после разрешения или отклонения. Так что лучше ставить перед ними операторы return, чтобы не было сюрпризов.

Метод then возвращает новый экземпляр Promise, поэтому можно использовать методы цепочки. После завершения первой функции обратного вызова возвращаемый результат будет передан в качестве параметра второй функции обратного вызова.

Возвращает значение обещания

get('https://api.github.com/users/github').then(res => {
  console.log('first:',res) //"first:真实请求到的值"
  return get('https://api.github.com/users/github');
}).then((res)=>{
  console.log('second:',res) //"second:真实请求到的值"
})

вернуть значение напрямую

//"first:真实请求到的值"

get('https://api.github.com/users/github').then(res => {
  console.log('first:',res) //"first:真实请求到的值"
  return 123;
}).then((res)=>{
  console.log('second:',res) //"second:123"
})

2. Promise.prototype.then()

Метод then определен для объекта-прототипа Promise.prototype. Роль заключается в добавлении функции обратного вызова к экземпляру Promise при изменении состояния.

3. Promise.prototype.catch()

3.1 Метод Promise.prototype.catch() является псевдонимом .then(null, rejection) или .then(undefined, rejection), который используется для указания функции обратного вызова при возникновении ошибки.

get('https://api.github.com/users/github').then(res => {
  throw new Error('error');
}).catch((res)=>{
  console.log('second:',res)  //second: Error: error
})

get('https://api.github.com/users/github').then(res => {
  throw new Error('error');
}).then(null,err => {
  console.log('err:',err)  //err: Error: error

})

Следующие два выражения эквивалентны

// 写法一
const promise = new Promise(function(resolve, reject) {
  try {
    throw new Error('test');  // throw方式
  } catch(e) {
    reject(e);
  }
});
promise.catch(function(error) {
  console.log(error);
});

// 写法二
const promise = new Promise(function(resolve, reject) {
  reject(new Error('test'));   // reject方式
});
promise.catch(function(error) {
  console.log(error);
});

3.2 Если состояние обещания было разрешено, выдавать ошибку нельзя.

const promise = new Promise(function(resolve, reject) {
  resolve('ok');
  throw new Error('test');  //无效的
});
promise
  .then(function(value) { console.log(value) })
  .catch(function(error) { console.log(error) });
// ok

3.3 Ошибка объекта Promise имеет свойство "пузыриться" и всегда будет передаваться в обратном направлении, пока не будет поймана. То есть ошибка всегда будет обнаружена следующим оператором catch.

get('https://api.github.com/users/github').then(res => {
  console.log('first:',res) //"first:真实请求到的值"
  return get('https://api.github.com/users/github');
}).then((res)=>{
  console.log('second:',res) //"second:真实请求到的值"
})

В приведенном выше коде есть три объекта Promise: один генерируется с помощью get(), а два — с помощью then().Ошибки, выдаваемые любой из них, будут перехватываться последней функцией catch().

3.4 Лучше не определять функцию обратного вызова состояния Reject в методе then(), а всегда использовать метод catch.

promise
  .then(function(data) {
    // success
  }, function(err) {
    // error
  });

// good
promise
  .then(function(data) { //cb
    // success
  })
  .catch(function(err) {
    // error
  });

Причина в том, что второй способ записи может поймать ошибку при выполнении предыдущего метода then,Также ближе к синхронной записи (try/catch)

3.5 Ошибки внутри промиса не повлияют на исполнителя кода вне промиса

Проще говоря, «Обещания съедают ошибки».

const someAsyncThing = function() {
  return new Promise(function(resolve, reject) {
    // 下面一行会报错,因为x没有声明
    resolve(x + 2);  //运行到此行抛出错误,但是不会退出进程、终止脚本执行
  });
};

someAsyncThing().then(function() {
  console.log('everything is great');
});

setTimeout(() => { console.log(123) }, 2000);  //依旧会执行
// Uncaught (in promise) ReferenceError: x is not defined
// 123

В приведенном выше примере лучше использовать catch

В отличие от традиционного блока кода try/catch, если с помощью метода catch() не указана функция обратного вызова для обработки ошибок, ошибка, выданная объектом Promise, не будет передана внешнему коду, то есть ответа не будет.

3.6 Обещание выдает ошибку в следующем раунде «цикла событий», которую можно перехватить

const promise = new Promise(function (resolve, reject) {
  resolve('ok');
  setTimeout(function () { throw new Error('test') }, 0)  
  //在下一轮“事件循环”再抛出错误,会执行,错误会冒泡到外层,能捕获
});
promise.then(function (value) { console.log(value) });
// ok
// Uncaught Error: test

3.7 Выдача ошибки в том же раунде "цикла событий"

Ошибка страницы, без улова, без вывода сообщения об ошибке (то есть без захвата), потому что разрешено, выдача ошибки недействительна

const promise = new Promise(function (resolve, reject) {
  resolve('ok');
  throw new Error('test') 
});
promise.then(function (value) { console.log(value) });
//ok

это тоже не зацепило

const promise = new Promise(function (resolve, reject) {
  resolve('ok');
  throw new Error('test') 
});
promise
.then( (value)=> { console.log(value) })
.catch(e => {console.log('err:',e)});
//ok

Если никакая решимость не может захватить

const promise = new Promise(function (resolve, reject) {
  throw new Error('test') 
});
promise
.then( (value)=> { console.log(value) })
.catch(e => {console.log('err:',e)});
//err: Error: test

3.8 Обычно рекомендуется, чтобы за объектом Promise следовал метод catch() для обработки ошибок, возникающих внутри объекта Promise.

Метод catch() возвращает объект Promise, поэтому метод then() можно вызвать позже.

const someAsyncThing = function() {
  return new Promise(function(resolve, reject) {
    // 下面一行会报错,因为x没有声明
    resolve(x + 2);
  });
};

someAsyncThing()
.catch(function(error) {
  console.log('oh no', error);
})
.then(function() {
  console.log('carry on');
});
// oh no [ReferenceError: x is not defined]
// carry on

Нет никакой ошибки, чтобы пропустить перехват и выполнить метод then() напрямую. На этом этапе, если в методе then() сообщается об ошибке, это не имеет ничего общего с предыдущим методом catch().

Promise.resolve()
.catch(function(error) {
  console.log('oh no', error);
})
.then(function() {
  console.log('carry on');
});
// carry on

3.9 catch может снова выдавать ошибки

const someAsyncThing = function() {
  return new Promise(function(resolve, reject) {
    // 下面一行会报错,因为x没有声明
    resolve(x + 2);
  });
};

someAsyncThing().then(function() {
  return someOtherAsyncThing();
}).catch(function(error) {
  console.log('oh no', error);
  // 下面一行会报错,因为 y 没有声明
  y + 2;
}).catch(function(error) {
  console.log('carry on', error);
});
// oh no [ReferenceError: x is not defined]
// carry on [ReferenceError: y is not defined]

Второй метод catch() используется для перехвата ошибок, вызванных предыдущим методом catch().

4. Promise.prototype.finally()

Независимо от конечного состояния обещания, после выполнения функции обратного вызова, указанной в then или catch, будет выполнена функция обратного вызова, указанная в методе finally.

// resolve 的值是 undefined
Promise.resolve(2).then(() => {}, () => {})

// resolve 的值是 2
Promise.resolve(2).finally(() => {})

// reject 的值是 undefined
Promise.reject(3).then(() => {}, () => {})

// reject 的值是 3
Promise.reject(3).finally(() => {})

5. Promise.all()

const p = Promise.all([p1, p2, p3]);

5.1 Параметр может не быть массивом, но он должен иметь интерфейс Iterator, и каждый возвращаемый элемент является экземпляром Promise.

5.2 Все состояния в параметре становятся выполненными, p становятся выполненными, возвращаемое значение, составляют массив

5.3 Если один из параметров отклонен, p становится отклоненным

5.4 Если параметр не является экземпляром Promise, сначала будет вызван метод Promise.resolve для преобразования параметра в экземпляр Promise.

5.5 Лучше всего следить за уловом

5.6 Если экземпляр Promise в качестве параметра определяет свой собственный метод catch, после его отклонения метод catch Promise.all() не будет запущен.

const p1 = new Promise((resolve, reject) => {
  resolve('hello');
})
.then(result => result)
.catch(e => e);

const p2 = new Promise((resolve, reject) => {
  throw new Error('报错了');
})
.then(result => result)
.catch(e => e);

Promise.all([p1, p2])
.then(result => console.log(result))
.catch(e => console.log(e));
// ["hello", Error: 报错了]

Будет сообщено только о первой ошибке

const p1 = new Promise((resolve, reject) => {
  throw new Error('报错了1');
})
.then(result => result);

const p2 = new Promise((resolve, reject) => {
  throw new Error('报错了2');
})
.then(result => result);

Promise.all([p1, p2])
.then(result => console.log(result))
.catch(e => console.log(e));
//Error: 报错了1

6. Promise.race()

const p = Promise.race([p1, p2, p3]);

6.1 Правила параметров такие же, как и для Promise.all()

6.2 Пока один экземпляр параметров изменяет состояние первым, состояние p изменяется соответственно.

6.3 Сценарий применения: Если результат не будет получен в течение указанного времени, будет сообщено об ошибке

const p = Promise.race([
  get('https://api.github.com/users/github'),
  new Promise(function (resolve, reject) {
    setTimeout(() => reject(new Error('request timeout')), 5000)
  })
]);

p
.then(console.log)
.catch(console.error)
//5m内没得到结果,就报错

7. Promise.allSettled()

7.1 Экземпляр оболочки не завершится, пока все экземпляры параметров не вернут результаты, выполненные или отклоненные.

7.2 Результатом является массив, а элементами являются объекты, как показано в следующем коде.

const resolved = Promise.resolve(42);
const rejected = Promise.reject(-1);

const allSettledPromise = Promise.allSettled([resolved, rejected]);

allSettledPromise.then(function (results) {
  console.log(results);
});
// [
//    { status: 'fulfilled', value: 42 },
//    { status: 'rejected', reason: -1 }
// ],找出

может пройти статус

7.3 Сценарий приложения: не заботьтесь о результатах асинхронных операций, заботьтесь только о том, завершились ли эти операции

8. Promise.any()

8.1 Пока один из экземпляров параметра становится выполненным состоянием, экземпляр упаковки становится выполненным состоянием

8.2 Если все экземпляры параметров будут отклонены, экземпляр оболочки будет отклонен

8.3 Он очень похож на метод Promise.race(), за исключением того, что он не завершается, потому что обещание отклоняется.

Чтобы поймать ошибки, вы можете использовать

9. Promise.resolve()

9.1 Существующие объекты можно преобразовать в объекты Promise

Promise.resolve('foo')
// 等价于
new Promise(resolve => resolve('foo'))

9.2 Параметры разделены на четыре случая

(1) Параметр является экземпляром Promise

Promise.resolve将不做任何修改、原封不动地返回这个实例。

(2) Параметр является доступным объектом

let thenable = {
  then: function(resolve, reject) {
    resolve(42);
  }
};

let p1 = Promise.resolve(thenable);
p1.then(function(value) {
  console.log(value);  // 42
});

После того, как метод then объекта thenable выполнен, состояние объекта p1 становится разрешенным, так что функция обратного вызова, указанная последним методом then, выполняется немедленно, и выводится 42.

(3) Параметр не является объектом с методом then или вообще не является объектом

Возвращает новый объект Promise с состоянием разрешения.

const p = Promise.resolve('Hello');

p.then(function (s){
  console.log(s)
});
// Hello

(4) без параметров

const p = Promise.resolve();

p.then(function () {
  // ...
});

10. Promise.reject()

10.1 Вернуть новый экземпляр промиса с состоянием отклонено

const p = Promise.reject('出错了');
// 等同于
const p = new Promise((resolve, reject) => reject('出错了'))

p.then(null, function (s) {
  console.log(s)
});
// 出错了

10.2 Какие параметры передаются и какие параметры используются как причины для отклонения

Примечание. Что несовместимо с методом Promise.resolve, так это то, что параметры метода Promise.reject() будут использоваться в качестве причины отклонения и станут параметрами последующего метода.

const thenable = {
  then(resolve, reject) {
    reject('出错了');
  }
};

Promise.reject(thenable)
.catch(e => {
  console.log(e === thenable)
})
// true

Promise.reject方法的参数是一个thenable对象,执行以后,后面catch方法的参数不是reject抛出的“出错了”这个字符串,而是thenable对象。

11. Примеры

  • загрузка изображения
function loadImageAsync(url) {
  return new Promise(function(resolve, reject) {
    const image = new Image();

    image.onload = function() {
      resolve(image);
    };

    image.onerror = function() {
      reject(new Error('Could not load image at ' + url));
    };

    image.src = url;
  });
}
  • асинхронный запрос
function get(url, params, config = {}) {    
  return new Promise((resolve, reject) => {
    axios.get(url, {
      params,
      ...config,
    }).then(res => {
      resolve(res.data);
    }).catch(err => {
      reject(err.data);
    })    
  });
}
  • Компоненты, которые инкапсулируют положительные и отрицательные кнопки, такие как messageBox

OK: утвердите обещание в состоянии разрешения Отмена: утвердите обещание в состоянии отклонения

buttonAction() {
   this.resolve();
}

2. ожидание/асинхронность

1. Особенности

  • Встроенный привод
  • Асинхронная функция возвращает объект Promise, а Promise можно использовать как параметр команды await.
  • Лучшая семантика, чем звездочка и доходность
  • После команды await это может быть объект Promise и значение примитивного типа (числовое, строковое и логическое, но оно будет автоматически преобразовано в немедленно разрешенный объект Promise)
  • await должно быть написано в асинхронной функции
  • Значение, возвращаемое оператором return внутри асинхронной функции, станет параметром функции обратного вызова метода then.
async function f() {
  return 'hello world';
}

f().then(v => console.log(v))
// "hello world"
  • Если объект Promise после любого оператора await переходит в состояние reject, вся асинхронная функция будет прервана.
async function f() {
  await Promise.reject('出错了');
  await Promise.resolve('hello world'); // 不会执行
}

Как это не прервать?

Способ 1: поместите первое ожидание в структуру try...catch

async function f() {
  try {  
    await Promise.reject('出错了'); 
  } catch(e) {
  }
  return await Promise.resolve('hello world');
}

f()
.then(v => console.log(v))
// hello world

Метод 2: за объектом Promise за ожиданием следует метод catch, который может обрабатывать возможные ошибки перед ним.

async function f() {
  await Promise.reject('出错了')
    .catch(e => console.log(e));
  return await Promise.resolve('hello world');
}

f()
.then(v => console.log(v))
// 出错了
// hello world

2. Написание функции

// 函数声明
async function foo() {}

// 函数表达式
const foo = async function () {};

// 对象的方法
let obj = { async foo() {} };
obj.foo().then(...)

// Class 的方法
class Storage {
  constructor() {
    this.cachePromise = caches.open('avatars');
  }

  async getAvatar(name) {
    const cache1 = await this.cachePromise;
    return name; //返回的值作为then中的参数
  }
}

const storage = new Storage();
storage.getAvatar('jake').then(…);

// 箭头函数
const foo = async () => {};

3. Параметры ожидания

(1) Если это не объект Promise, верните соответствующее значение напрямую

async function f() {
  // 等同于
  // return 123;
  return await 123;
}

f().then(v => console.log(v))
// 123

(2) За командой await следует объект thenable, который будет эквивалентен объекту Promise.

class Sleep {
  constructor(timeout) {
    this.timeout = timeout;
  }
  then(resolve, reject) {  //有then对象
    const startTime = Date.now();
    setTimeout(
      () => resolve(Date.now() - startTime),
      this.timeout
    );
  }
}

(async () => {
  const sleepTime = await new Sleep(1000);//直接视为new Promise处理
  console.log(sleepTime);
})();
// 1000

4. Механизм обработки ошибок

4.1 Функция ASYNC бросает ошибку внутри, что приведет к тому, что объект возврата Promise к состоянию отклонения.

async function f() {
  throw new Error('出错了');
  //await new Promise(function (resolve, reject) {
  //  throw new Error('出错了');
  //});
}

f().then(
  v => console.log(v),
  e => console.log(e)
)
// Error: 出错了

4.2 Чтобы предотвратить ошибки, используйте try catch, который не помешает следующей операции

async function f() {
  try {
    await new Promise(function (resolve, reject) {
      throw new Error('出错了');
    });
  } catch(e) {
  }
  return await('hello world');
}

4.3 Если имеется несколько команд await, их можно поместить в структуру try...catch

Почему существует ожидание/асинхронность Разница между ожиданием и обещанием

ловить ловить ошибки

Использовать с for...of

Примечание. Не запускайте одновременно, если вы можете выполнять одновременно, так что это также его недостаток.

Недостатки обещаний: Чтобы решить проблему ада обратного вызова,

Статья не про быстрый вывод, а про полировку.

5. На заметку

5.1 Лучше всего поместить команду await в блок try...catch или await, за которым следует catch

async function myFunction() {
  try {
    await somethingThatReturnsAPromise();
  } catch (err) {
    console.log(err);
  }
}

// 另一种写法

async function myFunction() {
  await somethingThatReturnsAPromise()
  .catch(function (err) {
    console.log(err);
  });
}

5.2 Асинхронные операции за несколькими командами await, если нет вторичной связи, лучше всего позволить им срабатывать одновременно, то есть можно обойтись без await

5.3 Команду await можно использовать только в асинхронных функциях, при использовании в обычных функциях будет выдано сообщение об ошибке

Для вторичной операции вы можете использовать for...of

async function dbFuc(db) {
  let docs = [{}, {}, {}];

  for (let doc of docs) {
    await db.post(doc);
  }
}

Лучше не использовать Foreach, потому что это будет одновременно

function dbFuc(db) { //这里不需要 async
  let docs = [{}, {}, {}];

  // 用forE爱吃,此写法是并发
  docs.forEach(async function (doc) {
    await db.post(doc); //await只能写在async函数之中
  });
}

Другой способ написания вторичной операции, вы можете использовать сокращение

async function dbFuc(db) {
  let docs = [{}, {}, {}];

  await docs.reduce(async (_, doc) => {
    await _;
    await db.post(doc);
  }, undefined);
}

Если вы действительно хотите, чтобы несколько запросов выполнялись одновременно, вы можете использовать метод Promise.all.

async function dbFuc(db) {
  let docs = [{}, {}, {}];
  let promises = docs.map((doc) => db.post(doc));

  let results = await Promise.all(promises);
  console.log(results);
}

// 或者使用下面的写法

async function dbFuc(db) {
  let docs = [{}, {}, {}];
  let promises = docs.map((doc) => db.post(doc));

  let results = [];
  for (let promise of promises) {
    results.push(await promise);
  }
  console.log(results);
}

5.4 асинхронные функции могут сохранить стек выполнения

const a = async () => {
  await b();
  c();
};
b()运行的时候,a()是暂停执行,上下文环境都保存着。
一旦b()或c()报错,错误堆栈将包括a()。

const a = () => {
  b().then(() => c());
};
函数a内部运行了一个异步任务b()。
当b()运行的时候,函数a()不会中断,而是继续执行。
等到b()运行结束,可能a()早就运行结束了,b()所在的上下文环境已经消失了。
如果b()或c()报错,错误堆栈将不包括a()。

6. Принцип реализации асинхронной функции

Принцип реализации асинхронной функции заключается в том, чтобы обернуть функцию-генератор и автоматический исполнитель в функцию.

async function fn(args) {
  // ...
}

// 等同于

function fn(args) {
  return spawn(function* () { //Generator 函数
    // ...
  });
}
//自动执行器
function spawn(genF) {
  return new Promise(function(resolve, reject) {
    const gen = genF();
    function step(nextF) {
      let next;
      try {
        next = nextF();
      } catch(e) {
        return reject(e);
      }
      if(next.done) {
        return resolve(next.value);
      }
      Promise.resolve(next.value).then(function(v) {
        step(function() { return gen.next(v); });
      }, function(e) {
        step(function() { return gen.throw(e); });
      });
    }
    step(function() { return gen.next(undefined); });
  });
}

7. Сравнение с другими методами асинхронной обработки

Предполагая, что в элементе DOM развернута серия анимаций, предыдущая анимация может быть запущена только после окончания предыдущей анимации. Если в одной из анимаций есть ошибка, она не будет выполнена, а будет возвращено возвращаемое значение последней успешно выполненной анимации.

Promise

function chainAnimationsPromise(elem, animations) {

  // 变量ret用来保存上一个动画的返回值
  let ret = null;

  // 新建一个空的Promise
  let p = Promise.resolve();

  // 使用then方法,添加所有动画
  for(let anim of animations) {
    p = p.then(function(val) {
      ret = val;
      return anim(elem); //返回的也是promise对象,注意这种写法思路
    });
  }

  // 返回一个部署了错误捕捉机制的Promise
  return p.catch(function(e) {
    /* 忽略错误,继续执行 */
  }).then(function() {
    return ret;
  });

}

Generator

function chainAnimationsGenerator(elem, animations) {

  return spawn(function*() {
    let ret = null;
    try {
      for(let anim of animations) {
        ret = yield anim(elem);
      }
    } catch(e) {
      /* 忽略错误,继续执行 */
    }
    return ret;
  });

}
写法复杂

async

async function chainAnimationsAsync(elem, animations) {
  let ret = null;
  //async最好加上try catch
  try {
    for(let anim of animations) {
      ret = await anim(elem);
    }
  } catch(e) {
    /* 忽略错误,继续执行 */
  }
  return ret;
}

Реализация асинхронных функций является наиболее лаконичной и наиболее семантически совместимой, почти без семантически нерелевантного кода. Он обеспечивает автоматический исполнитель в методе написания Генератора на уровне языка и не выставляет его пользователям, поэтому объем кода минимален.

8. Примеры

  • Последовательное выполнение асинхронных операций

Например, удаленно прочитать набор URL-адресов по очереди, а затем вывести результаты в порядке чтения.

Обещание сочетает в себе метод сокращения для отправки удаленных запросов, и результат выводится по порядку.

function logInOrder(urls) {
  // 远程读取所有URL
  const textPromises = urls.map(url => {
    return fetch(url).then(response => response.text());
  });

  // 按次序输出
  textPromises.reduce((chain, textPromise) => {
    return chain.then(() => textPromise)
      .then(text => console.log(text));
  }, Promise.resolve());
}

Метод await объединяется с for...of, во вторую очередь выдается удаленный запрос, а результат выводится по порядку.

async function logInOrder(urls) {
  for (const url of urls) {
    const response = await fetch(url);
    console.log(await response.text());
  }
}

Удаленные запросы выдаются одновременно, а результаты выводятся по порядку.

async function logInOrder(urls) {
  // 并发读取远程URL
  const textPromises = urls.map(async url => {
    const response = await fetch(url);
    return response.text();
  });

  // 按次序输出
  for (const textPromise of textPromises) {
    console.log(await textPromise);
  }
}

Дополнительное армирование

  • Сценарии использования: анимация (уменьшение вложенности), захват ошибок (единая локальная обработка), асинхронный запрос, img или srcipt, монитор загрузки onload и onerror

  • Promise.then также является обещанием

const p1 = new Promise((resolve, reject)=>{  
     resolve('success')
})

const p2 = p1.then((value)=>{console.log(value)}, (reason)=>{console.log(reason)})

//不加setTimeout
console.log(p1) //Promise{<resolve>:'success'} resolve状态的Promise
console.log(p2) //Promise{<pending>} pending状态的Promise    

因为p2是微任务还没有执行,所以是准备状态


//加上setTimeout
setTimeout(()=>{
    console.log(p1) //Promise{<resolve>:'success'} resolve状态的Promise
    console.log(p2) //Promise{<resolve>:undefined} resolve状态的Promise
})
因为先轮训微任务,再轮训宏任务,当轮训微任务的时候,状态发生改变,
  • Как написать метод then:
  1. class中的then方法会包装成promise
class A{
    then(resolve,reject){
        resolve() //此处加了改变状态,hhy才会被打印出来
    }
} 
async function get(){
    await new A();
    console.log('hhy')//hhy
}
get()
  1. статический метод в классе
class {
    static then(resolve,reject){}
}

3. Метод then объекта

{
    then(resolve, reject){
        resolve('这是对象')
    }
}
  • Возвратите промис в then, а затем следует обработка этого нового промиса, который можно использовать в обычное время, так что вложенность не будет слишком глубокой.
const p1 = new Promise((resolve, reject)=>{  
     resolve('success')
}).then((value)=>{
    console.log(value)
   // then中return 一个Promise,后面的then是对这个新的Promise的处理,平时可以这么使用,这样不会嵌套太深
   1、 直接new Promise
    return new Promise((resolve, reject)=>{
    resolve('success2')})
    //因为then也是一个Promise,所以也可以返回then
    2. then对象
    return {
        then(resolve, reject){
            resolve('这是对象')
        }
    }
    3. class中含有then函数
    class A{
        then(resolve,reject){
            resolve('这是对象') 
        }
    } 
    return new A()
    
    4. class静态方法
    return class{
       static then(resolve,reject){
            resolve('这是对象') 
        }
    }
    
}, (reason)=>{
    console.log(reason)
}).then((res)=>{
    console.log(res) //success2
})

catch异步处理是resolved状态, поэтому используйте Promise.all([A,B]), если A использует catch, независимо от того, отклонено ли предыдущее или разрешено, Promise.all может быть выполнен правильно, обещание отклонено, результат [undefined,resB]

const A = new Promise((resolve, reject)=>{
    setTimeout(()=>{
        reject('fail')
    })
}).catch((error)=>{console.log(error)})  // catch异步处理是resolved状态

const B = new Promise((resolve, reject)=>{
    setTimeout(()=>{
        resolve('success')
    })
}).catch((error)=>{console.log(error)})
Promise.all([A,B]).then(res=>{
    console.log(res) // [undefined, success]
}).catch((error)=>{console.log(error)})

Сопоставьте несколько асинхронных, вы можете использовать Promise.all, это не выполняется последовательно

let nums = [1,2,3,4,5]
let promises = nums.map((item,i)=>{
    return new Promise(resolve => {
        setTimeout(()=>{
            console.log(item) //1,2,3,4,5
            resolve(item)
        },1000*Math.random(i))
    })
})
Promise.all(promises).then((res)=>{
    console.log(res) //不知道顺序
})
  • выполнение очереди

Очередь: выполняется последовательно, ее принцип заключается в том, чтобы каждый раз возвращать новое обещание, затем использовать Promise.resolve() для создания обещания и выполнять обещание для обновления один раз.

//map封装Promise队列
function queue(nums){
    let promise = Promise.resolve();
    nums.map((item)=>{
        //关键:promise需要一直更新
        promise = promise.then(()=>{
        //这是原理:每次then返回一个新的Promise
            return new Promise(resolve => {
                setTimeout(()=>{
                    console.log(item) //1,2,3,4,5
                    resolve()
                },1000)
            })
        })
    })  
}
queue([1,2,3,4,5])
//reduce封装Promise队列
function queue(nums){
    nums.reduce((promise,item)=>{
        //关键:promise需要一直更新
        promise = promise.then(()=>{
        //这是原理:每次then返回一个新的Promise
            return new Promise(resolve => {
                setTimeout(()=>{
                    console.log(item) //1,2,3,4,5
                    resolve()
                },1000)
            })
        })
    },Promise.resolve())  
}
queue([1,2,3,4,5])
  • ожидать параллельного выполнения:
  1. Promise.all
//p1和p2都是返回Promise
async function test(){
    let res = await Promise.all([p1,p2])
}
  1. Не добавлять await при выполнении, добавить await для получения данных
//p1和p2都是返回Promise
async function test(){
    let h1 = p1(); 
    let h2 = p2();
    let h1val = await h1();
    let h2val = await h2();
}

Дальнейшее чтение:

Голодание 6. Жуань Ифэн.com/#docs/promi…

Голодание 6. Ruan Yifeng.com/#docs/A sync…