It's usually best to think about a high level language version first. If the number has n digits and is stored in array a, then we'd want:
char *p = a;
unsigned val = 0;
while (n > 0) {
  n--;
  val = 10 * val + (*p++ - '0');
}
So let's say %esi is p above %eax is val, and %ecx is n.
Translate line by line to keep it as simplistic and literal as possible:
  movl $n, %ecx
  movl $a, %esi
  xorl %eax, %eax     # val = 0
eval_while_cond:
  testl %ecx, %ecx    # if (n <= 0) 
  jle done            #   goto done
  subl $1, %ecx       # n--
  movl %eax, %ebx     # tmp1 = val
  imul $10, %ebx      # tmp1 = 10 * tmp1
  movzbl (%esi), %eax # tmp2 = *p
  addl $1, %esi       # p++
  subl $'0, %eax      # tmp2 = tmp2 - '0'
  addl %eax, %ebx     # val = tmp2 + tmp1
  jmp eval_while_cond
done:
  # result is now in %eax
Note this is just a general methodology for getting functional assembly language. This isn't optimized in any way. The tricks needed to get optimal performance are a whole other topic.
The other options is to use a compiler (e.g. https://godbolt.org/z/WzGYjePzx) with light optimization like -O1, -Og, or -Os to get somewhat-readable asm (although in this case they use not-totally-obvious tricks with LEA to multiply by 10 and subtract '0' (48), or they make a mess with weird loop conditions.)  See also How to remove "noise" from GCC/clang assembly output?